inlay_map.rs

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