inlay_map.rs

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