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                    .filter(|ch| *ch != '\r')
 476                    .take(len)
 477                    .collect::<String>();
 478                log::info!(
 479                    "creating inlay at buffer offset {} with bias {:?} and text {:?}",
 480                    position,
 481                    bias,
 482                    text
 483                );
 484                to_insert.push((
 485                    InlayId(post_inc(next_inlay_id)),
 486                    InlayProperties {
 487                        position: buffer_snapshot.anchor_at(position, bias),
 488                        text,
 489                    },
 490                ));
 491            } else {
 492                to_remove.push(*self.inlays_by_id.keys().choose(rng).unwrap());
 493            }
 494        }
 495        log::info!("removing inlays: {:?}", to_remove);
 496
 497        drop(snapshot);
 498        self.splice(to_remove, to_insert)
 499    }
 500}
 501
 502impl InlaySnapshot {
 503    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
 504        self.fold_snapshot.buffer_snapshot()
 505    }
 506
 507    pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
 508        let mut cursor = self
 509            .transforms
 510            .cursor::<(InlayOffset, (InlayPoint, FoldOffset))>();
 511        cursor.seek(&offset, Bias::Right, &());
 512        let overshoot = offset.0 - cursor.start().0 .0;
 513        match cursor.item() {
 514            Some(Transform::Isomorphic(_)) => {
 515                let fold_offset_start = cursor.start().1 .1;
 516                let fold_offset_end = FoldOffset(fold_offset_start.0 + overshoot);
 517                let fold_start = fold_offset_start.to_point(&self.fold_snapshot);
 518                let fold_end = fold_offset_end.to_point(&self.fold_snapshot);
 519                InlayPoint(cursor.start().1 .0 .0 + (fold_end.0 - fold_start.0))
 520            }
 521            Some(Transform::Inlay(inlay)) => {
 522                let overshoot = inlay.text.offset_to_point(overshoot);
 523                InlayPoint(cursor.start().1 .0 .0 + overshoot)
 524            }
 525            None => self.max_point(),
 526        }
 527    }
 528
 529    pub fn len(&self) -> InlayOffset {
 530        InlayOffset(self.transforms.summary().output.len)
 531    }
 532
 533    pub fn max_point(&self) -> InlayPoint {
 534        InlayPoint(self.transforms.summary().output.lines)
 535    }
 536
 537    pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
 538        let mut cursor = self
 539            .transforms
 540            .cursor::<(InlayPoint, (InlayOffset, FoldPoint))>();
 541        cursor.seek(&point, Bias::Right, &());
 542        let overshoot = point.0 - cursor.start().0 .0;
 543        match cursor.item() {
 544            Some(Transform::Isomorphic(_)) => {
 545                let fold_point_start = cursor.start().1 .1;
 546                let fold_point_end = FoldPoint(fold_point_start.0 + overshoot);
 547                let fold_start = fold_point_start.to_offset(&self.fold_snapshot);
 548                let fold_end = fold_point_end.to_offset(&self.fold_snapshot);
 549                InlayOffset(cursor.start().1 .0 .0 + (fold_end.0 - fold_start.0))
 550            }
 551            Some(Transform::Inlay(inlay)) => {
 552                let overshoot = inlay.text.point_to_offset(overshoot);
 553                InlayOffset(cursor.start().1 .0 .0 + overshoot)
 554            }
 555            None => self.len(),
 556        }
 557    }
 558
 559    pub fn chars_at(&self, start: InlayPoint) -> impl '_ + Iterator<Item = char> {
 560        self.chunks(self.to_offset(start)..self.len(), false, None, None)
 561            .flat_map(|chunk| chunk.text.chars())
 562    }
 563
 564    pub fn to_fold_point(&self, point: InlayPoint) -> FoldPoint {
 565        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
 566        cursor.seek(&point, Bias::Right, &());
 567        match cursor.item() {
 568            Some(Transform::Isomorphic(_)) => {
 569                let overshoot = point.0 - cursor.start().0 .0;
 570                FoldPoint(cursor.start().1 .0 + overshoot)
 571            }
 572            Some(Transform::Inlay(_)) => cursor.start().1,
 573            None => self.fold_snapshot.max_point(),
 574        }
 575    }
 576
 577    pub fn to_fold_offset(&self, offset: InlayOffset) -> FoldOffset {
 578        let mut cursor = self.transforms.cursor::<(InlayOffset, FoldOffset)>();
 579        cursor.seek(&offset, Bias::Right, &());
 580        match cursor.item() {
 581            Some(Transform::Isomorphic(_)) => {
 582                let overshoot = offset - cursor.start().0;
 583                cursor.start().1 + FoldOffset(overshoot.0)
 584            }
 585            Some(Transform::Inlay(_)) => cursor.start().1,
 586            None => self.fold_snapshot.len(),
 587        }
 588    }
 589
 590    pub fn to_inlay_point(&self, point: FoldPoint) -> InlayPoint {
 591        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>();
 592        cursor.seek(&point, Bias::Left, &());
 593        match cursor.item() {
 594            Some(Transform::Isomorphic(_)) => {
 595                let overshoot = point.0 - cursor.start().0 .0;
 596                InlayPoint(cursor.start().1 .0 + overshoot)
 597            }
 598            Some(Transform::Inlay(_)) => cursor.start().1,
 599            None => self.max_point(),
 600        }
 601    }
 602
 603    pub fn clip_point(&self, point: InlayPoint, bias: Bias) -> InlayPoint {
 604        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
 605        cursor.seek(&point, Bias::Left, &());
 606
 607        let mut bias = bias;
 608        let mut skipped_inlay = false;
 609        loop {
 610            match cursor.item() {
 611                Some(Transform::Isomorphic(transform)) => {
 612                    let overshoot = if skipped_inlay {
 613                        match bias {
 614                            Bias::Left => transform.lines,
 615                            Bias::Right => {
 616                                if transform.first_line_chars == 0 {
 617                                    Point::new(1, 0)
 618                                } else {
 619                                    Point::new(0, 1)
 620                                }
 621                            }
 622                        }
 623                    } else {
 624                        point.0 - cursor.start().0 .0
 625                    };
 626                    let fold_point = FoldPoint(cursor.start().1 .0 + overshoot);
 627                    let clipped_fold_point = self.fold_snapshot.clip_point(fold_point, bias);
 628                    let clipped_overshoot = clipped_fold_point.0 - cursor.start().1 .0;
 629                    return InlayPoint(cursor.start().0 .0 + clipped_overshoot);
 630                }
 631                Some(Transform::Inlay(_)) => skipped_inlay = true,
 632                None => match bias {
 633                    Bias::Left => return Default::default(),
 634                    Bias::Right => bias = Bias::Left,
 635                },
 636            }
 637
 638            if bias == Bias::Left {
 639                cursor.prev(&());
 640            } else {
 641                cursor.next(&());
 642            }
 643        }
 644    }
 645
 646    pub fn text_summary_for_range(&self, range: Range<InlayPoint>) -> TextSummary {
 647        let mut summary = TextSummary::default();
 648
 649        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
 650        cursor.seek(&range.start, Bias::Right, &());
 651
 652        let overshoot = range.start.0 - cursor.start().0 .0;
 653        match cursor.item() {
 654            Some(Transform::Isomorphic(_)) => {
 655                let fold_start = cursor.start().1 .0;
 656                let suffix_start = FoldPoint(fold_start + overshoot);
 657                let suffix_end = FoldPoint(
 658                    fold_start + (cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0),
 659                );
 660                summary = self
 661                    .fold_snapshot
 662                    .text_summary_for_range(suffix_start..suffix_end);
 663                cursor.next(&());
 664            }
 665            Some(Transform::Inlay(inlay)) => {
 666                let suffix_start = inlay.text.point_to_offset(overshoot);
 667                let suffix_end = inlay.text.point_to_offset(
 668                    cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0,
 669                );
 670                summary = inlay.text.cursor(suffix_start).summary(suffix_end);
 671                cursor.next(&());
 672            }
 673            None => {}
 674        }
 675
 676        if range.end > cursor.start().0 {
 677            summary += cursor
 678                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
 679                .output;
 680
 681            let overshoot = range.end.0 - cursor.start().0 .0;
 682            match cursor.item() {
 683                Some(Transform::Isomorphic(_)) => {
 684                    let prefix_start = cursor.start().1;
 685                    let prefix_end = FoldPoint(prefix_start.0 + overshoot);
 686                    summary += self
 687                        .fold_snapshot
 688                        .text_summary_for_range(prefix_start..prefix_end);
 689                }
 690                Some(Transform::Inlay(inlay)) => {
 691                    let prefix_end = inlay.text.point_to_offset(overshoot);
 692                    summary += inlay.text.cursor(0).summary::<TextSummary>(prefix_end);
 693                }
 694                None => {}
 695            }
 696        }
 697
 698        summary
 699    }
 700
 701    pub fn buffer_rows<'a>(&'a self, row: u32) -> InlayBufferRows<'a> {
 702        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>();
 703        let inlay_point = InlayPoint::new(row, 0);
 704        cursor.seek(&inlay_point, Bias::Left, &());
 705
 706        let mut fold_point = cursor.start().1;
 707        let fold_row = if row == 0 {
 708            0
 709        } else {
 710            match cursor.item() {
 711                Some(Transform::Isomorphic(_)) => {
 712                    fold_point.0 += inlay_point.0 - cursor.start().0 .0;
 713                    fold_point.row()
 714                }
 715                _ => cmp::min(fold_point.row() + 1, self.fold_snapshot.max_point().row()),
 716            }
 717        };
 718
 719        InlayBufferRows {
 720            transforms: cursor,
 721            inlay_row: inlay_point.row(),
 722            fold_rows: self.fold_snapshot.buffer_rows(fold_row),
 723        }
 724    }
 725
 726    pub fn line_len(&self, row: u32) -> u32 {
 727        let line_start = self.to_offset(InlayPoint::new(row, 0)).0;
 728        let line_end = if row >= self.max_point().row() {
 729            self.len().0
 730        } else {
 731            self.to_offset(InlayPoint::new(row + 1, 0)).0 - 1
 732        };
 733        (line_end - line_start) as u32
 734    }
 735
 736    pub fn chunks<'a>(
 737        &'a self,
 738        range: Range<InlayOffset>,
 739        language_aware: bool,
 740        text_highlights: Option<&'a TextHighlights>,
 741        inlay_highlight_style: Option<HighlightStyle>,
 742    ) -> InlayChunks<'a> {
 743        let mut cursor = self.transforms.cursor::<(InlayOffset, FoldOffset)>();
 744        cursor.seek(&range.start, Bias::Right, &());
 745
 746        let fold_range = self.to_fold_offset(range.start)..self.to_fold_offset(range.end);
 747        let fold_chunks = self
 748            .fold_snapshot
 749            .chunks(fold_range, language_aware, text_highlights);
 750
 751        InlayChunks {
 752            transforms: cursor,
 753            fold_chunks,
 754            inlay_chunks: None,
 755            fold_chunk: None,
 756            output_offset: range.start,
 757            max_output_offset: range.end,
 758            highlight_style: inlay_highlight_style,
 759        }
 760    }
 761
 762    #[cfg(test)]
 763    pub fn text(&self) -> String {
 764        self.chunks(Default::default()..self.len(), false, None, None)
 765            .map(|chunk| chunk.text)
 766            .collect()
 767    }
 768
 769    fn check_invariants(&self) {
 770        #[cfg(any(debug_assertions, feature = "test-support"))]
 771        {
 772            assert_eq!(
 773                self.transforms.summary().input,
 774                self.fold_snapshot.text_summary()
 775            );
 776        }
 777    }
 778}
 779
 780fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
 781    if summary.len == 0 {
 782        return;
 783    }
 784
 785    let mut summary = Some(summary);
 786    sum_tree.update_last(
 787        |transform| {
 788            if let Transform::Isomorphic(transform) = transform {
 789                *transform += summary.take().unwrap();
 790            }
 791        },
 792        &(),
 793    );
 794
 795    if let Some(summary) = summary {
 796        sum_tree.push(Transform::Isomorphic(summary), &());
 797    }
 798}
 799
 800#[cfg(test)]
 801mod tests {
 802    use super::*;
 803    use crate::{display_map::fold_map::FoldMap, MultiBuffer};
 804    use gpui::AppContext;
 805    use rand::prelude::*;
 806    use settings::SettingsStore;
 807    use std::env;
 808    use text::Patch;
 809    use util::post_inc;
 810
 811    #[gpui::test]
 812    fn test_basic_inlays(cx: &mut AppContext) {
 813        let buffer = MultiBuffer::build_simple("abcdefghi", cx);
 814        let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
 815        let (fold_map, fold_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
 816        let (mut inlay_map, inlay_snapshot) = InlayMap::new(fold_snapshot.clone());
 817        assert_eq!(inlay_snapshot.text(), "abcdefghi");
 818        let mut next_inlay_id = 0;
 819
 820        let (inlay_snapshot, _) = inlay_map.splice(
 821            Vec::new(),
 822            vec![(
 823                InlayId(post_inc(&mut next_inlay_id)),
 824                InlayProperties {
 825                    position: buffer.read(cx).snapshot(cx).anchor_after(3),
 826                    text: "|123|",
 827                },
 828            )],
 829        );
 830        assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
 831        assert_eq!(
 832            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 0)),
 833            InlayPoint::new(0, 0)
 834        );
 835        assert_eq!(
 836            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 1)),
 837            InlayPoint::new(0, 1)
 838        );
 839        assert_eq!(
 840            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 2)),
 841            InlayPoint::new(0, 2)
 842        );
 843        assert_eq!(
 844            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 3)),
 845            InlayPoint::new(0, 3)
 846        );
 847        assert_eq!(
 848            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 4)),
 849            InlayPoint::new(0, 9)
 850        );
 851        assert_eq!(
 852            inlay_snapshot.to_inlay_point(FoldPoint::new(0, 5)),
 853            InlayPoint::new(0, 10)
 854        );
 855        assert_eq!(
 856            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
 857            InlayPoint::new(0, 0)
 858        );
 859        assert_eq!(
 860            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
 861            InlayPoint::new(0, 0)
 862        );
 863        assert_eq!(
 864            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
 865            InlayPoint::new(0, 3)
 866        );
 867        assert_eq!(
 868            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
 869            InlayPoint::new(0, 3)
 870        );
 871        assert_eq!(
 872            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
 873            InlayPoint::new(0, 3)
 874        );
 875        assert_eq!(
 876            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
 877            InlayPoint::new(0, 9)
 878        );
 879
 880        // Edits before or after the inlay should not affect it.
 881        buffer.update(cx, |buffer, cx| {
 882            buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
 883        });
 884        let (fold_snapshot, fold_edits) = fold_map.read(
 885            buffer.read(cx).snapshot(cx),
 886            buffer_edits.consume().into_inner(),
 887        );
 888        let (inlay_snapshot, _) = inlay_map.sync(fold_snapshot.clone(), fold_edits);
 889        assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
 890
 891        // An edit surrounding the inlay should invalidate it.
 892        buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
 893        let (fold_snapshot, fold_edits) = fold_map.read(
 894            buffer.read(cx).snapshot(cx),
 895            buffer_edits.consume().into_inner(),
 896        );
 897        let (inlay_snapshot, _) = inlay_map.sync(fold_snapshot.clone(), fold_edits);
 898        assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
 899
 900        let (inlay_snapshot, _) = inlay_map.splice(
 901            Vec::new(),
 902            vec![
 903                (
 904                    InlayId(post_inc(&mut next_inlay_id)),
 905                    InlayProperties {
 906                        position: buffer.read(cx).snapshot(cx).anchor_before(3),
 907                        text: "|123|",
 908                    },
 909                ),
 910                (
 911                    InlayId(post_inc(&mut next_inlay_id)),
 912                    InlayProperties {
 913                        position: buffer.read(cx).snapshot(cx).anchor_after(3),
 914                        text: "|456|",
 915                    },
 916                ),
 917            ],
 918        );
 919        assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi");
 920
 921        // Edits ending where the inlay starts should not move it if it has a left bias.
 922        buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
 923        let (fold_snapshot, fold_edits) = fold_map.read(
 924            buffer.read(cx).snapshot(cx),
 925            buffer_edits.consume().into_inner(),
 926        );
 927        let (inlay_snapshot, _) = inlay_map.sync(fold_snapshot.clone(), fold_edits);
 928        assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
 929
 930        // The inlays can be manually removed.
 931        let (inlay_snapshot, _) = inlay_map
 932            .splice::<String>(inlay_map.inlays_by_id.keys().copied().collect(), Vec::new());
 933        assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi");
 934    }
 935
 936    #[gpui::test]
 937    fn test_buffer_rows(cx: &mut AppContext) {
 938        let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
 939        let (_, fold_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
 940        let (mut inlay_map, inlay_snapshot) = InlayMap::new(fold_snapshot.clone());
 941        assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
 942
 943        let (inlay_snapshot, _) = inlay_map.splice(
 944            Vec::new(),
 945            vec![
 946                (
 947                    InlayId(0),
 948                    InlayProperties {
 949                        position: buffer.read(cx).snapshot(cx).anchor_before(0),
 950                        text: "|123|\n",
 951                    },
 952                ),
 953                (
 954                    InlayId(1),
 955                    InlayProperties {
 956                        position: buffer.read(cx).snapshot(cx).anchor_before(4),
 957                        text: "|456|",
 958                    },
 959                ),
 960                (
 961                    InlayId(1),
 962                    InlayProperties {
 963                        position: buffer.read(cx).snapshot(cx).anchor_before(7),
 964                        text: "\n|567|\n",
 965                    },
 966                ),
 967            ],
 968        );
 969        assert_eq!(inlay_snapshot.text(), "|123|\nabc\n|456|def\n|567|\n\nghi");
 970        assert_eq!(
 971            inlay_snapshot.buffer_rows(0).collect::<Vec<_>>(),
 972            vec![Some(0), None, Some(1), None, None, Some(2)]
 973        );
 974    }
 975
 976    #[gpui::test(iterations = 100)]
 977    fn test_random_inlays(cx: &mut AppContext, mut rng: StdRng) {
 978        init_test(cx);
 979
 980        let operations = env::var("OPERATIONS")
 981            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 982            .unwrap_or(10);
 983
 984        let len = rng.gen_range(0..30);
 985        let buffer = if rng.gen() {
 986            let text = util::RandomCharIter::new(&mut rng)
 987                .take(len)
 988                .collect::<String>();
 989            MultiBuffer::build_simple(&text, cx)
 990        } else {
 991            MultiBuffer::build_random(&mut rng, cx)
 992        };
 993        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
 994        log::info!("buffer text: {:?}", buffer_snapshot.text());
 995
 996        let (mut fold_map, mut fold_snapshot) = FoldMap::new(buffer_snapshot.clone());
 997        let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(fold_snapshot.clone());
 998        let mut next_inlay_id = 0;
 999
1000        for _ in 0..operations {
1001            let mut fold_edits = Patch::default();
1002            let mut inlay_edits = Patch::default();
1003
1004            let mut prev_inlay_text = inlay_snapshot.text();
1005            let mut buffer_edits = Vec::new();
1006            match rng.gen_range(0..=100) {
1007                0..=29 => {
1008                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1009                    log::info!("mutated text: {:?}", snapshot.text());
1010                    inlay_edits = Patch::new(edits);
1011                }
1012                30..=59 => {
1013                    for (_, edits) in fold_map.randomly_mutate(&mut rng) {
1014                        fold_edits = fold_edits.compose(edits);
1015                    }
1016                }
1017                _ => buffer.update(cx, |buffer, cx| {
1018                    let subscription = buffer.subscribe();
1019                    let edit_count = rng.gen_range(1..=5);
1020                    buffer.randomly_mutate(&mut rng, edit_count, cx);
1021                    buffer_snapshot = buffer.snapshot(cx);
1022                    let edits = subscription.consume().into_inner();
1023                    log::info!("editing {:?}", edits);
1024                    buffer_edits.extend(edits);
1025                }),
1026            };
1027
1028            let (new_fold_snapshot, new_fold_edits) =
1029                fold_map.read(buffer_snapshot.clone(), buffer_edits);
1030            fold_snapshot = new_fold_snapshot;
1031            fold_edits = fold_edits.compose(new_fold_edits);
1032            let (new_inlay_snapshot, new_inlay_edits) =
1033                inlay_map.sync(fold_snapshot.clone(), fold_edits.into_inner());
1034            inlay_snapshot = new_inlay_snapshot;
1035            inlay_edits = inlay_edits.compose(new_inlay_edits);
1036
1037            log::info!("buffer text: {:?}", buffer_snapshot.text());
1038            log::info!("folds text: {:?}", fold_snapshot.text());
1039            log::info!("inlay text: {:?}", inlay_snapshot.text());
1040
1041            let inlays = inlay_map
1042                .inlays
1043                .iter()
1044                .filter(|inlay| inlay.position.is_valid(&buffer_snapshot))
1045                .map(|inlay| {
1046                    let buffer_point = inlay.position.to_point(&buffer_snapshot);
1047                    let fold_point = fold_snapshot.to_fold_point(buffer_point, Bias::Left);
1048                    let fold_offset = fold_point.to_offset(&fold_snapshot);
1049                    (fold_offset, inlay.clone())
1050                })
1051                .collect::<Vec<_>>();
1052            let mut expected_text = Rope::from(fold_snapshot.text().as_str());
1053            for (offset, inlay) in inlays.into_iter().rev() {
1054                expected_text.replace(offset.0..offset.0, &inlay.text.to_string());
1055            }
1056            assert_eq!(inlay_snapshot.text(), expected_text.to_string());
1057
1058            let expected_buffer_rows = inlay_snapshot.buffer_rows(0).collect::<Vec<_>>();
1059            assert_eq!(
1060                expected_buffer_rows.len() as u32,
1061                expected_text.max_point().row + 1
1062            );
1063            for row_start in 0..expected_buffer_rows.len() {
1064                assert_eq!(
1065                    inlay_snapshot
1066                        .buffer_rows(row_start as u32)
1067                        .collect::<Vec<_>>(),
1068                    &expected_buffer_rows[row_start..],
1069                    "incorrect buffer rows starting at {}",
1070                    row_start
1071                );
1072            }
1073
1074            for _ in 0..5 {
1075                let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
1076                end = expected_text.clip_offset(end, Bias::Right);
1077                let mut start = rng.gen_range(0..=end);
1078                start = expected_text.clip_offset(start, Bias::Right);
1079
1080                let actual_text = inlay_snapshot
1081                    .chunks(InlayOffset(start)..InlayOffset(end), false, None, None)
1082                    .map(|chunk| chunk.text)
1083                    .collect::<String>();
1084                assert_eq!(
1085                    actual_text,
1086                    expected_text.slice(start..end).to_string(),
1087                    "incorrect text in range {:?}",
1088                    start..end
1089                );
1090
1091                let start_point = InlayPoint(expected_text.offset_to_point(start));
1092                let end_point = InlayPoint(expected_text.offset_to_point(end));
1093                assert_eq!(
1094                    inlay_snapshot.text_summary_for_range(start_point..end_point),
1095                    expected_text.slice(start..end).summary()
1096                );
1097            }
1098
1099            for edit in inlay_edits {
1100                prev_inlay_text.replace_range(
1101                    edit.new.start.0..edit.new.start.0 + edit.old_len().0,
1102                    &inlay_snapshot.text()[edit.new.start.0..edit.new.end.0],
1103                );
1104            }
1105            assert_eq!(prev_inlay_text, inlay_snapshot.text());
1106
1107            assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0);
1108            assert_eq!(expected_text.len(), inlay_snapshot.len().0);
1109
1110            let mut inlay_point = InlayPoint::default();
1111            let mut inlay_offset = InlayOffset::default();
1112            for ch in expected_text.chars() {
1113                assert_eq!(
1114                    inlay_snapshot.to_offset(inlay_point),
1115                    inlay_offset,
1116                    "invalid to_offset({:?})",
1117                    inlay_point
1118                );
1119                assert_eq!(
1120                    inlay_snapshot.to_point(inlay_offset),
1121                    inlay_point,
1122                    "invalid to_point({:?})",
1123                    inlay_offset
1124                );
1125                assert_eq!(
1126                    inlay_snapshot.to_inlay_point(inlay_snapshot.to_fold_point(inlay_point)),
1127                    inlay_snapshot.clip_point(inlay_point, Bias::Left),
1128                    "to_fold_point({:?}) = {:?}",
1129                    inlay_point,
1130                    inlay_snapshot.to_fold_point(inlay_point),
1131                );
1132
1133                let mut bytes = [0; 4];
1134                for byte in ch.encode_utf8(&mut bytes).as_bytes() {
1135                    inlay_offset.0 += 1;
1136                    if *byte == b'\n' {
1137                        inlay_point.0 += Point::new(1, 0);
1138                    } else {
1139                        inlay_point.0 += Point::new(0, 1);
1140                    }
1141
1142                    let clipped_left_point = inlay_snapshot.clip_point(inlay_point, Bias::Left);
1143                    let clipped_right_point = inlay_snapshot.clip_point(inlay_point, Bias::Right);
1144                    assert!(
1145                        clipped_left_point <= clipped_right_point,
1146                        "clipped left point {:?} is greater than clipped right point {:?}",
1147                        clipped_left_point,
1148                        clipped_right_point
1149                    );
1150
1151                    // Ensure the clipped points are at valid text locations.
1152                    assert_eq!(
1153                        clipped_left_point.0,
1154                        expected_text.clip_point(clipped_left_point.0, Bias::Left)
1155                    );
1156                    assert_eq!(
1157                        clipped_right_point.0,
1158                        expected_text.clip_point(clipped_right_point.0, Bias::Right)
1159                    );
1160
1161                    // Ensure the clipped points never overshoot the end of the map.
1162                    assert!(clipped_left_point <= inlay_snapshot.max_point());
1163                    assert!(clipped_right_point <= inlay_snapshot.max_point());
1164                }
1165            }
1166        }
1167    }
1168
1169    fn init_test(cx: &mut AppContext) {
1170        cx.set_global(SettingsStore::test(cx));
1171        theme::init((), cx);
1172    }
1173}