inlay_hint_cache.rs

   1use std::{
   2    cmp,
   3    ops::{ControlFlow, Range},
   4    sync::Arc,
   5    time::Duration,
   6};
   7
   8use crate::{
   9    display_map::Inlay, Anchor, Editor, ExcerptId, InlayId, MultiBuffer, MultiBufferSnapshot,
  10};
  11use anyhow::Context;
  12use clock::Global;
  13use futures::future;
  14use gpui::{Model, ModelContext, Task, ViewContext};
  15use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
  16use parking_lot::RwLock;
  17use project::{InlayHint, ResolveState};
  18
  19use collections::{hash_map, HashMap, HashSet};
  20use language::language_settings::InlayHintSettings;
  21use smol::lock::Semaphore;
  22use sum_tree::Bias;
  23use text::{ToOffset, ToPoint};
  24use util::post_inc;
  25
  26pub struct InlayHintCache {
  27    hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
  28    allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
  29    version: usize,
  30    pub(super) enabled: bool,
  31    update_tasks: HashMap<ExcerptId, TasksForRanges>,
  32    lsp_request_limiter: Arc<Semaphore>,
  33}
  34
  35#[derive(Debug)]
  36struct TasksForRanges {
  37    tasks: Vec<Task<()>>,
  38    sorted_ranges: Vec<Range<language::Anchor>>,
  39}
  40
  41#[derive(Debug)]
  42pub struct CachedExcerptHints {
  43    version: usize,
  44    buffer_version: Global,
  45    buffer_id: u64,
  46    ordered_hints: Vec<InlayId>,
  47    hints_by_id: HashMap<InlayId, InlayHint>,
  48}
  49
  50#[derive(Debug, Clone, Copy)]
  51pub enum InvalidationStrategy {
  52    RefreshRequested,
  53    BufferEdited,
  54    None,
  55}
  56
  57#[derive(Debug, Default)]
  58pub struct InlaySplice {
  59    pub to_remove: Vec<InlayId>,
  60    pub to_insert: Vec<Inlay>,
  61}
  62
  63#[derive(Debug)]
  64struct ExcerptHintsUpdate {
  65    excerpt_id: ExcerptId,
  66    remove_from_visible: Vec<InlayId>,
  67    remove_from_cache: HashSet<InlayId>,
  68    add_to_cache: Vec<InlayHint>,
  69}
  70
  71#[derive(Debug, Clone, Copy)]
  72struct ExcerptQuery {
  73    buffer_id: u64,
  74    excerpt_id: ExcerptId,
  75    cache_version: usize,
  76    invalidate: InvalidationStrategy,
  77    reason: &'static str,
  78}
  79
  80impl InvalidationStrategy {
  81    fn should_invalidate(&self) -> bool {
  82        matches!(
  83            self,
  84            InvalidationStrategy::RefreshRequested | InvalidationStrategy::BufferEdited
  85        )
  86    }
  87}
  88
  89impl TasksForRanges {
  90    fn new(query_ranges: QueryRanges, task: Task<()>) -> Self {
  91        let mut sorted_ranges = Vec::new();
  92        sorted_ranges.extend(query_ranges.before_visible);
  93        sorted_ranges.extend(query_ranges.visible);
  94        sorted_ranges.extend(query_ranges.after_visible);
  95        Self {
  96            tasks: vec![task],
  97            sorted_ranges,
  98        }
  99    }
 100
 101    fn update_cached_tasks(
 102        &mut self,
 103        buffer_snapshot: &BufferSnapshot,
 104        query_ranges: QueryRanges,
 105        invalidate: InvalidationStrategy,
 106        spawn_task: impl FnOnce(QueryRanges) -> Task<()>,
 107    ) {
 108        let query_ranges = if invalidate.should_invalidate() {
 109            self.tasks.clear();
 110            self.sorted_ranges.clear();
 111            query_ranges
 112        } else {
 113            let mut non_cached_query_ranges = query_ranges;
 114            non_cached_query_ranges.before_visible = non_cached_query_ranges
 115                .before_visible
 116                .into_iter()
 117                .flat_map(|query_range| {
 118                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
 119                })
 120                .collect();
 121            non_cached_query_ranges.visible = non_cached_query_ranges
 122                .visible
 123                .into_iter()
 124                .flat_map(|query_range| {
 125                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
 126                })
 127                .collect();
 128            non_cached_query_ranges.after_visible = non_cached_query_ranges
 129                .after_visible
 130                .into_iter()
 131                .flat_map(|query_range| {
 132                    self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
 133                })
 134                .collect();
 135            non_cached_query_ranges
 136        };
 137
 138        if !query_ranges.is_empty() {
 139            self.tasks.push(spawn_task(query_ranges));
 140        }
 141    }
 142
 143    fn remove_cached_ranges_from_query(
 144        &mut self,
 145        buffer_snapshot: &BufferSnapshot,
 146        query_range: Range<language::Anchor>,
 147    ) -> Vec<Range<language::Anchor>> {
 148        let mut ranges_to_query = Vec::new();
 149        let mut latest_cached_range = None::<&mut Range<language::Anchor>>;
 150        for cached_range in self
 151            .sorted_ranges
 152            .iter_mut()
 153            .skip_while(|cached_range| {
 154                cached_range
 155                    .end
 156                    .cmp(&query_range.start, buffer_snapshot)
 157                    .is_lt()
 158            })
 159            .take_while(|cached_range| {
 160                cached_range
 161                    .start
 162                    .cmp(&query_range.end, buffer_snapshot)
 163                    .is_le()
 164            })
 165        {
 166            match latest_cached_range {
 167                Some(latest_cached_range) => {
 168                    if latest_cached_range.end.offset.saturating_add(1) < cached_range.start.offset
 169                    {
 170                        ranges_to_query.push(latest_cached_range.end..cached_range.start);
 171                        cached_range.start = latest_cached_range.end;
 172                    }
 173                }
 174                None => {
 175                    if query_range
 176                        .start
 177                        .cmp(&cached_range.start, buffer_snapshot)
 178                        .is_lt()
 179                    {
 180                        ranges_to_query.push(query_range.start..cached_range.start);
 181                        cached_range.start = query_range.start;
 182                    }
 183                }
 184            }
 185            latest_cached_range = Some(cached_range);
 186        }
 187
 188        match latest_cached_range {
 189            Some(latest_cached_range) => {
 190                if latest_cached_range.end.offset.saturating_add(1) < query_range.end.offset {
 191                    ranges_to_query.push(latest_cached_range.end..query_range.end);
 192                    latest_cached_range.end = query_range.end;
 193                }
 194            }
 195            None => {
 196                ranges_to_query.push(query_range.clone());
 197                self.sorted_ranges.push(query_range);
 198                self.sorted_ranges
 199                    .sort_by(|range_a, range_b| range_a.start.cmp(&range_b.start, buffer_snapshot));
 200            }
 201        }
 202
 203        ranges_to_query
 204    }
 205
 206    fn invalidate_range(&mut self, buffer: &BufferSnapshot, range: &Range<language::Anchor>) {
 207        self.sorted_ranges = self
 208            .sorted_ranges
 209            .drain(..)
 210            .filter_map(|mut cached_range| {
 211                if cached_range.start.cmp(&range.end, buffer).is_gt()
 212                    || cached_range.end.cmp(&range.start, buffer).is_lt()
 213                {
 214                    Some(vec![cached_range])
 215                } else if cached_range.start.cmp(&range.start, buffer).is_ge()
 216                    && cached_range.end.cmp(&range.end, buffer).is_le()
 217                {
 218                    None
 219                } else if range.start.cmp(&cached_range.start, buffer).is_ge()
 220                    && range.end.cmp(&cached_range.end, buffer).is_le()
 221                {
 222                    Some(vec![
 223                        cached_range.start..range.start,
 224                        range.end..cached_range.end,
 225                    ])
 226                } else if cached_range.start.cmp(&range.start, buffer).is_ge() {
 227                    cached_range.start = range.end;
 228                    Some(vec![cached_range])
 229                } else {
 230                    cached_range.end = range.start;
 231                    Some(vec![cached_range])
 232                }
 233            })
 234            .flatten()
 235            .collect();
 236    }
 237}
 238
 239impl InlayHintCache {
 240    pub fn new(inlay_hint_settings: InlayHintSettings) -> Self {
 241        Self {
 242            allowed_hint_kinds: inlay_hint_settings.enabled_inlay_hint_kinds(),
 243            enabled: inlay_hint_settings.enabled,
 244            hints: HashMap::default(),
 245            update_tasks: HashMap::default(),
 246            version: 0,
 247            lsp_request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_LSP_REQUESTS)),
 248        }
 249    }
 250
 251    pub fn update_settings(
 252        &mut self,
 253        multi_buffer: &Model<MultiBuffer>,
 254        new_hint_settings: InlayHintSettings,
 255        visible_hints: Vec<Inlay>,
 256        cx: &mut ViewContext<Editor>,
 257    ) -> ControlFlow<Option<InlaySplice>> {
 258        let new_allowed_hint_kinds = new_hint_settings.enabled_inlay_hint_kinds();
 259        match (self.enabled, new_hint_settings.enabled) {
 260            (false, false) => {
 261                self.allowed_hint_kinds = new_allowed_hint_kinds;
 262                ControlFlow::Break(None)
 263            }
 264            (true, true) => {
 265                if new_allowed_hint_kinds == self.allowed_hint_kinds {
 266                    ControlFlow::Break(None)
 267                } else {
 268                    let new_splice = self.new_allowed_hint_kinds_splice(
 269                        multi_buffer,
 270                        &visible_hints,
 271                        &new_allowed_hint_kinds,
 272                        cx,
 273                    );
 274                    if new_splice.is_some() {
 275                        self.version += 1;
 276                        self.allowed_hint_kinds = new_allowed_hint_kinds;
 277                    }
 278                    ControlFlow::Break(new_splice)
 279                }
 280            }
 281            (true, false) => {
 282                self.enabled = new_hint_settings.enabled;
 283                self.allowed_hint_kinds = new_allowed_hint_kinds;
 284                if self.hints.is_empty() {
 285                    ControlFlow::Break(None)
 286                } else {
 287                    self.clear();
 288                    ControlFlow::Break(Some(InlaySplice {
 289                        to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(),
 290                        to_insert: Vec::new(),
 291                    }))
 292                }
 293            }
 294            (false, true) => {
 295                self.enabled = new_hint_settings.enabled;
 296                self.allowed_hint_kinds = new_allowed_hint_kinds;
 297                ControlFlow::Continue(())
 298            }
 299        }
 300    }
 301
 302    pub fn spawn_hint_refresh(
 303        &mut self,
 304        reason: &'static str,
 305        excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
 306        invalidate: InvalidationStrategy,
 307        cx: &mut ViewContext<Editor>,
 308    ) -> Option<InlaySplice> {
 309        if !self.enabled {
 310            return None;
 311        }
 312
 313        let mut invalidated_hints = Vec::new();
 314        if invalidate.should_invalidate() {
 315            self.update_tasks
 316                .retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
 317            self.hints.retain(|cached_excerpt, cached_hints| {
 318                let retain = excerpts_to_query.contains_key(cached_excerpt);
 319                if !retain {
 320                    invalidated_hints.extend(cached_hints.read().ordered_hints.iter().copied());
 321                }
 322                retain
 323            });
 324        }
 325        if excerpts_to_query.is_empty() && invalidated_hints.is_empty() {
 326            return None;
 327        }
 328
 329        let cache_version = self.version + 1;
 330        cx.spawn(|editor, mut cx| async move {
 331            editor
 332                .update(&mut cx, |editor, cx| {
 333                    spawn_new_update_tasks(
 334                        editor,
 335                        reason,
 336                        excerpts_to_query,
 337                        invalidate,
 338                        cache_version,
 339                        cx,
 340                    )
 341                })
 342                .ok();
 343        })
 344        .detach();
 345
 346        if invalidated_hints.is_empty() {
 347            None
 348        } else {
 349            Some(InlaySplice {
 350                to_remove: invalidated_hints,
 351                to_insert: Vec::new(),
 352            })
 353        }
 354    }
 355
 356    fn new_allowed_hint_kinds_splice(
 357        &self,
 358        multi_buffer: &Model<MultiBuffer>,
 359        visible_hints: &[Inlay],
 360        new_kinds: &HashSet<Option<InlayHintKind>>,
 361        cx: &mut ViewContext<Editor>,
 362    ) -> Option<InlaySplice> {
 363        let old_kinds = &self.allowed_hint_kinds;
 364        if new_kinds == old_kinds {
 365            return None;
 366        }
 367
 368        let mut to_remove = Vec::new();
 369        let mut to_insert = Vec::new();
 370        let mut shown_hints_to_remove = visible_hints.iter().fold(
 371            HashMap::<ExcerptId, Vec<(Anchor, InlayId)>>::default(),
 372            |mut current_hints, inlay| {
 373                current_hints
 374                    .entry(inlay.position.excerpt_id)
 375                    .or_default()
 376                    .push((inlay.position, inlay.id));
 377                current_hints
 378            },
 379        );
 380
 381        let multi_buffer = multi_buffer.read(cx);
 382        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 383
 384        for (excerpt_id, excerpt_cached_hints) in &self.hints {
 385            let shown_excerpt_hints_to_remove =
 386                shown_hints_to_remove.entry(*excerpt_id).or_default();
 387            let excerpt_cached_hints = excerpt_cached_hints.read();
 388            let mut excerpt_cache = excerpt_cached_hints.ordered_hints.iter().fuse().peekable();
 389            shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
 390                let Some(buffer) = shown_anchor
 391                    .buffer_id
 392                    .and_then(|buffer_id| multi_buffer.buffer(buffer_id))
 393                else {
 394                    return false;
 395                };
 396                let buffer_snapshot = buffer.read(cx).snapshot();
 397                loop {
 398                    match excerpt_cache.peek() {
 399                        Some(&cached_hint_id) => {
 400                            let cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
 401                            if cached_hint_id == shown_hint_id {
 402                                excerpt_cache.next();
 403                                return !new_kinds.contains(&cached_hint.kind);
 404                            }
 405
 406                            match cached_hint
 407                                .position
 408                                .cmp(&shown_anchor.text_anchor, &buffer_snapshot)
 409                            {
 410                                cmp::Ordering::Less | cmp::Ordering::Equal => {
 411                                    if !old_kinds.contains(&cached_hint.kind)
 412                                        && new_kinds.contains(&cached_hint.kind)
 413                                    {
 414                                        to_insert.push(Inlay::hint(
 415                                            cached_hint_id.id(),
 416                                            multi_buffer_snapshot.anchor_in_excerpt(
 417                                                *excerpt_id,
 418                                                cached_hint.position,
 419                                            ),
 420                                            &cached_hint,
 421                                        ));
 422                                    }
 423                                    excerpt_cache.next();
 424                                }
 425                                cmp::Ordering::Greater => return true,
 426                            }
 427                        }
 428                        None => return true,
 429                    }
 430                }
 431            });
 432
 433            for cached_hint_id in excerpt_cache {
 434                let maybe_missed_cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
 435                let cached_hint_kind = maybe_missed_cached_hint.kind;
 436                if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) {
 437                    to_insert.push(Inlay::hint(
 438                        cached_hint_id.id(),
 439                        multi_buffer_snapshot
 440                            .anchor_in_excerpt(*excerpt_id, maybe_missed_cached_hint.position),
 441                        &maybe_missed_cached_hint,
 442                    ));
 443                }
 444            }
 445        }
 446
 447        to_remove.extend(
 448            shown_hints_to_remove
 449                .into_values()
 450                .flatten()
 451                .map(|(_, hint_id)| hint_id),
 452        );
 453        if to_remove.is_empty() && to_insert.is_empty() {
 454            None
 455        } else {
 456            Some(InlaySplice {
 457                to_remove,
 458                to_insert,
 459            })
 460        }
 461    }
 462
 463    pub fn remove_excerpts(&mut self, excerpts_removed: Vec<ExcerptId>) -> Option<InlaySplice> {
 464        let mut to_remove = Vec::new();
 465        for excerpt_to_remove in excerpts_removed {
 466            self.update_tasks.remove(&excerpt_to_remove);
 467            if let Some(cached_hints) = self.hints.remove(&excerpt_to_remove) {
 468                let cached_hints = cached_hints.read();
 469                to_remove.extend(cached_hints.ordered_hints.iter().copied());
 470            }
 471        }
 472        if to_remove.is_empty() {
 473            None
 474        } else {
 475            self.version += 1;
 476            Some(InlaySplice {
 477                to_remove,
 478                to_insert: Vec::new(),
 479            })
 480        }
 481    }
 482
 483    pub fn clear(&mut self) {
 484        if !self.update_tasks.is_empty() || !self.hints.is_empty() {
 485            self.version += 1;
 486        }
 487        self.update_tasks.clear();
 488        self.hints.clear();
 489    }
 490
 491    pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option<InlayHint> {
 492        self.hints
 493            .get(&excerpt_id)?
 494            .read()
 495            .hints_by_id
 496            .get(&hint_id)
 497            .cloned()
 498    }
 499
 500    pub fn hints(&self) -> Vec<InlayHint> {
 501        let mut hints = Vec::new();
 502        for excerpt_hints in self.hints.values() {
 503            let excerpt_hints = excerpt_hints.read();
 504            hints.extend(
 505                excerpt_hints
 506                    .ordered_hints
 507                    .iter()
 508                    .map(|id| &excerpt_hints.hints_by_id[id])
 509                    .cloned(),
 510            );
 511        }
 512        hints
 513    }
 514
 515    pub fn version(&self) -> usize {
 516        self.version
 517    }
 518
 519    pub fn spawn_hint_resolve(
 520        &self,
 521        buffer_id: u64,
 522        excerpt_id: ExcerptId,
 523        id: InlayId,
 524        cx: &mut ViewContext<'_, Editor>,
 525    ) {
 526        if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
 527            let mut guard = excerpt_hints.write();
 528            if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
 529                if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state {
 530                    let hint_to_resolve = cached_hint.clone();
 531                    let server_id = *server_id;
 532                    cached_hint.resolve_state = ResolveState::Resolving;
 533                    drop(guard);
 534                    cx.spawn(|editor, mut cx| async move {
 535                        let resolved_hint_task = editor.update(&mut cx, |editor, cx| {
 536                            editor
 537                                .buffer()
 538                                .read(cx)
 539                                .buffer(buffer_id)
 540                                .and_then(|buffer| {
 541                                    let project = editor.project.as_ref()?;
 542                                    Some(project.update(cx, |project, cx| {
 543                                        project.resolve_inlay_hint(
 544                                            hint_to_resolve,
 545                                            buffer,
 546                                            server_id,
 547                                            cx,
 548                                        )
 549                                    }))
 550                                })
 551                        })?;
 552                        if let Some(resolved_hint_task) = resolved_hint_task {
 553                            let mut resolved_hint =
 554                                resolved_hint_task.await.context("hint resolve task")?;
 555                            editor.update(&mut cx, |editor, _| {
 556                                if let Some(excerpt_hints) =
 557                                    editor.inlay_hint_cache.hints.get(&excerpt_id)
 558                                {
 559                                    let mut guard = excerpt_hints.write();
 560                                    if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
 561                                        if cached_hint.resolve_state == ResolveState::Resolving {
 562                                            resolved_hint.resolve_state = ResolveState::Resolved;
 563                                            *cached_hint = resolved_hint;
 564                                        }
 565                                    }
 566                                }
 567                            })?;
 568                        }
 569
 570                        anyhow::Ok(())
 571                    })
 572                    .detach_and_log_err(cx);
 573                }
 574            }
 575        }
 576    }
 577}
 578
 579fn spawn_new_update_tasks(
 580    editor: &mut Editor,
 581    reason: &'static str,
 582    excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
 583    invalidate: InvalidationStrategy,
 584    update_cache_version: usize,
 585    cx: &mut ViewContext<'_, Editor>,
 586) {
 587    let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
 588    for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
 589        excerpts_to_query
 590    {
 591        if excerpt_visible_range.is_empty() {
 592            continue;
 593        }
 594        let buffer = excerpt_buffer.read(cx);
 595        let buffer_id = buffer.remote_id();
 596        let buffer_snapshot = buffer.snapshot();
 597        if buffer_snapshot
 598            .version()
 599            .changed_since(&new_task_buffer_version)
 600        {
 601            continue;
 602        }
 603
 604        let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
 605        if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
 606            let cached_excerpt_hints = cached_excerpt_hints.read();
 607            let cached_buffer_version = &cached_excerpt_hints.buffer_version;
 608            if cached_excerpt_hints.version > update_cache_version
 609                || cached_buffer_version.changed_since(&new_task_buffer_version)
 610            {
 611                continue;
 612            }
 613        };
 614
 615        let (multi_buffer_snapshot, Some(query_ranges)) =
 616            editor.buffer.update(cx, |multi_buffer, cx| {
 617                (
 618                    multi_buffer.snapshot(cx),
 619                    determine_query_ranges(
 620                        multi_buffer,
 621                        excerpt_id,
 622                        &excerpt_buffer,
 623                        excerpt_visible_range,
 624                        cx,
 625                    ),
 626                )
 627            })
 628        else {
 629            return;
 630        };
 631        let query = ExcerptQuery {
 632            buffer_id,
 633            excerpt_id,
 634            cache_version: update_cache_version,
 635            invalidate,
 636            reason,
 637        };
 638
 639        let new_update_task = |query_ranges| {
 640            new_update_task(
 641                query,
 642                query_ranges,
 643                multi_buffer_snapshot,
 644                buffer_snapshot.clone(),
 645                Arc::clone(&visible_hints),
 646                cached_excerpt_hints,
 647                Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
 648                cx,
 649            )
 650        };
 651
 652        match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
 653            hash_map::Entry::Occupied(mut o) => {
 654                o.get_mut().update_cached_tasks(
 655                    &buffer_snapshot,
 656                    query_ranges,
 657                    invalidate,
 658                    new_update_task,
 659                );
 660            }
 661            hash_map::Entry::Vacant(v) => {
 662                v.insert(TasksForRanges::new(
 663                    query_ranges.clone(),
 664                    new_update_task(query_ranges),
 665                ));
 666            }
 667        }
 668    }
 669}
 670
 671#[derive(Debug, Clone)]
 672struct QueryRanges {
 673    before_visible: Vec<Range<language::Anchor>>,
 674    visible: Vec<Range<language::Anchor>>,
 675    after_visible: Vec<Range<language::Anchor>>,
 676}
 677
 678impl QueryRanges {
 679    fn is_empty(&self) -> bool {
 680        self.before_visible.is_empty() && self.visible.is_empty() && self.after_visible.is_empty()
 681    }
 682}
 683
 684fn determine_query_ranges(
 685    multi_buffer: &mut MultiBuffer,
 686    excerpt_id: ExcerptId,
 687    excerpt_buffer: &Model<Buffer>,
 688    excerpt_visible_range: Range<usize>,
 689    cx: &mut ModelContext<'_, MultiBuffer>,
 690) -> Option<QueryRanges> {
 691    let full_excerpt_range = multi_buffer
 692        .excerpts_for_buffer(excerpt_buffer, cx)
 693        .into_iter()
 694        .find(|(id, _)| id == &excerpt_id)
 695        .map(|(_, range)| range.context)?;
 696    let buffer = excerpt_buffer.read(cx);
 697    let snapshot = buffer.snapshot();
 698    let excerpt_visible_len = excerpt_visible_range.end - excerpt_visible_range.start;
 699
 700    let visible_range = if excerpt_visible_range.start == excerpt_visible_range.end {
 701        return None;
 702    } else {
 703        vec![
 704            buffer.anchor_before(snapshot.clip_offset(excerpt_visible_range.start, Bias::Left))
 705                ..buffer.anchor_after(snapshot.clip_offset(excerpt_visible_range.end, Bias::Right)),
 706        ]
 707    };
 708
 709    let full_excerpt_range_end_offset = full_excerpt_range.end.to_offset(&snapshot);
 710    let after_visible_range_start = excerpt_visible_range
 711        .end
 712        .saturating_add(1)
 713        .min(full_excerpt_range_end_offset)
 714        .min(buffer.len());
 715    let after_visible_range = if after_visible_range_start == full_excerpt_range_end_offset {
 716        Vec::new()
 717    } else {
 718        let after_range_end_offset = after_visible_range_start
 719            .saturating_add(excerpt_visible_len)
 720            .min(full_excerpt_range_end_offset)
 721            .min(buffer.len());
 722        vec![
 723            buffer.anchor_before(snapshot.clip_offset(after_visible_range_start, Bias::Left))
 724                ..buffer.anchor_after(snapshot.clip_offset(after_range_end_offset, Bias::Right)),
 725        ]
 726    };
 727
 728    let full_excerpt_range_start_offset = full_excerpt_range.start.to_offset(&snapshot);
 729    let before_visible_range_end = excerpt_visible_range
 730        .start
 731        .saturating_sub(1)
 732        .max(full_excerpt_range_start_offset);
 733    let before_visible_range = if before_visible_range_end == full_excerpt_range_start_offset {
 734        Vec::new()
 735    } else {
 736        let before_range_start_offset = before_visible_range_end
 737            .saturating_sub(excerpt_visible_len)
 738            .max(full_excerpt_range_start_offset);
 739        vec![
 740            buffer.anchor_before(snapshot.clip_offset(before_range_start_offset, Bias::Left))
 741                ..buffer.anchor_after(snapshot.clip_offset(before_visible_range_end, Bias::Right)),
 742        ]
 743    };
 744
 745    Some(QueryRanges {
 746        before_visible: before_visible_range,
 747        visible: visible_range,
 748        after_visible: after_visible_range,
 749    })
 750}
 751
 752const MAX_CONCURRENT_LSP_REQUESTS: usize = 5;
 753const INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS: u64 = 400;
 754
 755fn new_update_task(
 756    query: ExcerptQuery,
 757    query_ranges: QueryRanges,
 758    multi_buffer_snapshot: MultiBufferSnapshot,
 759    buffer_snapshot: BufferSnapshot,
 760    visible_hints: Arc<Vec<Inlay>>,
 761    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
 762    lsp_request_limiter: Arc<Semaphore>,
 763    cx: &mut ViewContext<'_, Editor>,
 764) -> Task<()> {
 765    cx.spawn(|editor, mut cx| async move {
 766        let closure_cx = cx.clone();
 767        let fetch_and_update_hints = |invalidate, range| {
 768            fetch_and_update_hints(
 769                editor.clone(),
 770                multi_buffer_snapshot.clone(),
 771                buffer_snapshot.clone(),
 772                Arc::clone(&visible_hints),
 773                cached_excerpt_hints.as_ref().map(Arc::clone),
 774                query,
 775                invalidate,
 776                range,
 777                Arc::clone(&lsp_request_limiter),
 778                closure_cx.clone(),
 779            )
 780        };
 781        let visible_range_update_results = future::join_all(query_ranges.visible.into_iter().map(
 782            |visible_range| async move {
 783                (
 784                    visible_range.clone(),
 785                    fetch_and_update_hints(query.invalidate.should_invalidate(), visible_range)
 786                        .await,
 787                )
 788            },
 789        ))
 790        .await;
 791
 792        let hint_delay = cx.background().timer(Duration::from_millis(
 793            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
 794        ));
 795
 796        let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
 797            log::error!("inlay hint update task for range {range:?} failed: {e:#}");
 798            editor
 799                .update(&mut cx, |editor, _| {
 800                    if let Some(task_ranges) = editor
 801                        .inlay_hint_cache
 802                        .update_tasks
 803                        .get_mut(&query.excerpt_id)
 804                    {
 805                        task_ranges.invalidate_range(&buffer_snapshot, &range);
 806                    }
 807                })
 808                .ok()
 809        };
 810
 811        for (range, result) in visible_range_update_results {
 812            if let Err(e) = result {
 813                query_range_failed(&range, e);
 814            }
 815        }
 816
 817        hint_delay.await;
 818        let invisible_range_update_results = future::join_all(
 819            query_ranges
 820                .before_visible
 821                .into_iter()
 822                .chain(query_ranges.after_visible.into_iter())
 823                .map(|invisible_range| async move {
 824                    (
 825                        invisible_range.clone(),
 826                        fetch_and_update_hints(false, invisible_range).await,
 827                    )
 828                }),
 829        )
 830        .await;
 831        for (range, result) in invisible_range_update_results {
 832            if let Err(e) = result {
 833                query_range_failed(&range, e);
 834            }
 835        }
 836    })
 837}
 838
 839// async fn fetch_and_update_hints(
 840//     editor: gpui::WeakView<Editor>,
 841//     multi_buffer_snapshot: MultiBufferSnapshot,
 842//     buffer_snapshot: BufferSnapshot,
 843//     visible_hints: Arc<Vec<Inlay>>,
 844//     cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
 845//     query: ExcerptQuery,
 846//     invalidate: bool,
 847//     fetch_range: Range<language::Anchor>,
 848//     lsp_request_limiter: Arc<Semaphore>,
 849//     mut cx: gpui::AsyncAppContext,
 850// ) -> anyhow::Result<()> {
 851//     let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
 852//         (None, false)
 853//     } else {
 854//         match lsp_request_limiter.try_acquire() {
 855//             Some(guard) => (Some(guard), false),
 856//             None => (Some(lsp_request_limiter.acquire().await), true),
 857//         }
 858//     };
 859//     let fetch_range_to_log =
 860//         fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
 861//     let inlay_hints_fetch_task = editor
 862//         .update(&mut cx, |editor, cx| {
 863//             if got_throttled {
 864//                 let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) {
 865//                     Some((_, _, current_visible_range)) => {
 866//                         let visible_offset_length = current_visible_range.len();
 867//                         let double_visible_range = current_visible_range
 868//                             .start
 869//                             .saturating_sub(visible_offset_length)
 870//                             ..current_visible_range
 871//                                 .end
 872//                                 .saturating_add(visible_offset_length)
 873//                                 .min(buffer_snapshot.len());
 874//                         !double_visible_range
 875//                             .contains(&fetch_range.start.to_offset(&buffer_snapshot))
 876//                             && !double_visible_range
 877//                                 .contains(&fetch_range.end.to_offset(&buffer_snapshot))
 878//                     },
 879//                     None => true,
 880//                 };
 881//                 if query_not_around_visible_range {
 882//                     log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
 883//                     if let Some(task_ranges) = editor
 884//                         .inlay_hint_cache
 885//                         .update_tasks
 886//                         .get_mut(&query.excerpt_id)
 887//                     {
 888//                         task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
 889//                     }
 890//                     return None;
 891//                 }
 892//             }
 893//             editor
 894//                 .buffer()
 895//                 .read(cx)
 896//                 .buffer(query.buffer_id)
 897//                 .and_then(|buffer| {
 898//                     let project = editor.project.as_ref()?;
 899//                     Some(project.update(cx, |project, cx| {
 900//                         project.inlay_hints(buffer, fetch_range.clone(), cx)
 901//                     }))
 902//                 })
 903//         })
 904//         .ok()
 905//         .flatten();
 906//     let new_hints = match inlay_hints_fetch_task {
 907//         Some(fetch_task) => {
 908//             log::debug!(
 909//                 "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
 910//                 query_reason = query.reason,
 911//             );
 912//             log::trace!(
 913//                 "Currently visible hints: {visible_hints:?}, cached hints present: {}",
 914//                 cached_excerpt_hints.is_some(),
 915//             );
 916//             fetch_task.await.context("inlay hint fetch task")?
 917//         }
 918//         None => return Ok(()),
 919//     };
 920//     drop(lsp_request_guard);
 921//     log::debug!(
 922//         "Fetched {} hints for range {fetch_range_to_log:?}",
 923//         new_hints.len()
 924//     );
 925//     log::trace!("Fetched hints: {new_hints:?}");
 926
 927//     let background_task_buffer_snapshot = buffer_snapshot.clone();
 928//     let backround_fetch_range = fetch_range.clone();
 929//     let new_update = cx
 930//         .background()
 931//         .spawn(async move {
 932//             calculate_hint_updates(
 933//                 query.excerpt_id,
 934//                 invalidate,
 935//                 backround_fetch_range,
 936//                 new_hints,
 937//                 &background_task_buffer_snapshot,
 938//                 cached_excerpt_hints,
 939//                 &visible_hints,
 940//             )
 941//         })
 942//         .await;
 943//     if let Some(new_update) = new_update {
 944//         log::debug!(
 945//             "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
 946//             new_update.remove_from_visible.len(),
 947//             new_update.remove_from_cache.len(),
 948//             new_update.add_to_cache.len()
 949//         );
 950//         log::trace!("New update: {new_update:?}");
 951//         editor
 952//             .update(&mut cx, |editor, cx| {
 953//                 apply_hint_update(
 954//                     editor,
 955//                     new_update,
 956//                     query,
 957//                     invalidate,
 958//                     buffer_snapshot,
 959//                     multi_buffer_snapshot,
 960//                     cx,
 961//                 );
 962//             })
 963//             .ok();
 964//     }
 965//     Ok(())
 966// }
 967
 968fn calculate_hint_updates(
 969    excerpt_id: ExcerptId,
 970    invalidate: bool,
 971    fetch_range: Range<language::Anchor>,
 972    new_excerpt_hints: Vec<InlayHint>,
 973    buffer_snapshot: &BufferSnapshot,
 974    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
 975    visible_hints: &[Inlay],
 976) -> Option<ExcerptHintsUpdate> {
 977    let mut add_to_cache = Vec::<InlayHint>::new();
 978    let mut excerpt_hints_to_persist = HashMap::default();
 979    for new_hint in new_excerpt_hints {
 980        if !contains_position(&fetch_range, new_hint.position, buffer_snapshot) {
 981            continue;
 982        }
 983        let missing_from_cache = match &cached_excerpt_hints {
 984            Some(cached_excerpt_hints) => {
 985                let cached_excerpt_hints = cached_excerpt_hints.read();
 986                match cached_excerpt_hints
 987                    .ordered_hints
 988                    .binary_search_by(|probe| {
 989                        cached_excerpt_hints.hints_by_id[probe]
 990                            .position
 991                            .cmp(&new_hint.position, buffer_snapshot)
 992                    }) {
 993                    Ok(ix) => {
 994                        let mut missing_from_cache = true;
 995                        for id in &cached_excerpt_hints.ordered_hints[ix..] {
 996                            let cached_hint = &cached_excerpt_hints.hints_by_id[id];
 997                            if new_hint
 998                                .position
 999                                .cmp(&cached_hint.position, buffer_snapshot)
1000                                .is_gt()
1001                            {
1002                                break;
1003                            }
1004                            if cached_hint == &new_hint {
1005                                excerpt_hints_to_persist.insert(*id, cached_hint.kind);
1006                                missing_from_cache = false;
1007                            }
1008                        }
1009                        missing_from_cache
1010                    }
1011                    Err(_) => true,
1012                }
1013            }
1014            None => true,
1015        };
1016        if missing_from_cache {
1017            add_to_cache.push(new_hint);
1018        }
1019    }
1020
1021    let mut remove_from_visible = Vec::new();
1022    let mut remove_from_cache = HashSet::default();
1023    if invalidate {
1024        remove_from_visible.extend(
1025            visible_hints
1026                .iter()
1027                .filter(|hint| hint.position.excerpt_id == excerpt_id)
1028                .map(|inlay_hint| inlay_hint.id)
1029                .filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)),
1030        );
1031
1032        if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
1033            let cached_excerpt_hints = cached_excerpt_hints.read();
1034            remove_from_cache.extend(
1035                cached_excerpt_hints
1036                    .ordered_hints
1037                    .iter()
1038                    .filter(|cached_inlay_id| {
1039                        !excerpt_hints_to_persist.contains_key(cached_inlay_id)
1040                    })
1041                    .copied(),
1042            );
1043        }
1044    }
1045
1046    if remove_from_visible.is_empty() && remove_from_cache.is_empty() && add_to_cache.is_empty() {
1047        None
1048    } else {
1049        Some(ExcerptHintsUpdate {
1050            excerpt_id,
1051            remove_from_visible,
1052            remove_from_cache,
1053            add_to_cache,
1054        })
1055    }
1056}
1057
1058fn contains_position(
1059    range: &Range<language::Anchor>,
1060    position: language::Anchor,
1061    buffer_snapshot: &BufferSnapshot,
1062) -> bool {
1063    range.start.cmp(&position, buffer_snapshot).is_le()
1064        && range.end.cmp(&position, buffer_snapshot).is_ge()
1065}
1066
1067fn apply_hint_update(
1068    editor: &mut Editor,
1069    new_update: ExcerptHintsUpdate,
1070    query: ExcerptQuery,
1071    invalidate: bool,
1072    buffer_snapshot: BufferSnapshot,
1073    multi_buffer_snapshot: MultiBufferSnapshot,
1074    cx: &mut ViewContext<'_, Editor>,
1075) {
1076    let cached_excerpt_hints = editor
1077        .inlay_hint_cache
1078        .hints
1079        .entry(new_update.excerpt_id)
1080        .or_insert_with(|| {
1081            Arc::new(RwLock::new(CachedExcerptHints {
1082                version: query.cache_version,
1083                buffer_version: buffer_snapshot.version().clone(),
1084                buffer_id: query.buffer_id,
1085                ordered_hints: Vec::new(),
1086                hints_by_id: HashMap::default(),
1087            }))
1088        });
1089    let mut cached_excerpt_hints = cached_excerpt_hints.write();
1090    match query.cache_version.cmp(&cached_excerpt_hints.version) {
1091        cmp::Ordering::Less => return,
1092        cmp::Ordering::Greater | cmp::Ordering::Equal => {
1093            cached_excerpt_hints.version = query.cache_version;
1094        }
1095    }
1096
1097    let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
1098    cached_excerpt_hints
1099        .ordered_hints
1100        .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
1101    cached_excerpt_hints
1102        .hints_by_id
1103        .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
1104    let mut splice = InlaySplice {
1105        to_remove: new_update.remove_from_visible,
1106        to_insert: Vec::new(),
1107    };
1108    for new_hint in new_update.add_to_cache {
1109        let insert_position = match cached_excerpt_hints
1110            .ordered_hints
1111            .binary_search_by(|probe| {
1112                cached_excerpt_hints.hints_by_id[probe]
1113                    .position
1114                    .cmp(&new_hint.position, &buffer_snapshot)
1115            }) {
1116            Ok(i) => {
1117                let mut insert_position = Some(i);
1118                for id in &cached_excerpt_hints.ordered_hints[i..] {
1119                    let cached_hint = &cached_excerpt_hints.hints_by_id[id];
1120                    if new_hint
1121                        .position
1122                        .cmp(&cached_hint.position, &buffer_snapshot)
1123                        .is_gt()
1124                    {
1125                        break;
1126                    }
1127                    if cached_hint.text() == new_hint.text() {
1128                        insert_position = None;
1129                        break;
1130                    }
1131                }
1132                insert_position
1133            }
1134            Err(i) => Some(i),
1135        };
1136
1137        if let Some(insert_position) = insert_position {
1138            let new_inlay_id = post_inc(&mut editor.next_inlay_id);
1139            if editor
1140                .inlay_hint_cache
1141                .allowed_hint_kinds
1142                .contains(&new_hint.kind)
1143            {
1144                let new_hint_position =
1145                    multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position);
1146                splice
1147                    .to_insert
1148                    .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
1149            }
1150            let new_id = InlayId::Hint(new_inlay_id);
1151            cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
1152            cached_excerpt_hints
1153                .ordered_hints
1154                .insert(insert_position, new_id);
1155            cached_inlays_changed = true;
1156        }
1157    }
1158    cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
1159    drop(cached_excerpt_hints);
1160
1161    if invalidate {
1162        let mut outdated_excerpt_caches = HashSet::default();
1163        for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
1164            let excerpt_hints = excerpt_hints.read();
1165            if excerpt_hints.buffer_id == query.buffer_id
1166                && excerpt_id != &query.excerpt_id
1167                && buffer_snapshot
1168                    .version()
1169                    .changed_since(&excerpt_hints.buffer_version)
1170            {
1171                outdated_excerpt_caches.insert(*excerpt_id);
1172                splice
1173                    .to_remove
1174                    .extend(excerpt_hints.ordered_hints.iter().copied());
1175            }
1176        }
1177        cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
1178        editor
1179            .inlay_hint_cache
1180            .hints
1181            .retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
1182    }
1183
1184    let InlaySplice {
1185        to_remove,
1186        to_insert,
1187    } = splice;
1188    let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
1189    if cached_inlays_changed || displayed_inlays_changed {
1190        editor.inlay_hint_cache.version += 1;
1191    }
1192    if displayed_inlays_changed {
1193        editor.splice_inlay_hints(to_remove, to_insert, cx)
1194    }
1195}
1196
1197// #[cfg(test)]
1198// pub mod tests {
1199//     use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
1200
1201//     use crate::{
1202//         scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
1203//         serde_json::json,
1204//         ExcerptRange,
1205//     };
1206//     use futures::StreamExt;
1207//     use gpui::{executor::Deterministic, TestAppContext, View};
1208//     use itertools::Itertools;
1209//     use language::{
1210//         language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
1211//     };
1212//     use lsp::FakeLanguageServer;
1213//     use parking_lot::Mutex;
1214//     use project::{FakeFs, Project};
1215//     use settings::SettingsStore;
1216//     use text::{Point, ToPoint};
1217//     use workspace::Workspace;
1218
1219//     use crate::editor_tests::update_test_language_settings;
1220
1221//     use super::*;
1222
1223//     #[gpui::test]
1224//     async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
1225//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
1226//         init_test(cx, |settings| {
1227//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1228//                 enabled: true,
1229//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1230//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
1231//                 show_other_hints: allowed_hint_kinds.contains(&None),
1232//             })
1233//         });
1234
1235//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1236//         let lsp_request_count = Arc::new(AtomicU32::new(0));
1237//         fake_server
1238//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1239//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
1240//                 async move {
1241//                     assert_eq!(
1242//                         params.text_document.uri,
1243//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
1244//                     );
1245//                     let current_call_id =
1246//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1247//                     let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
1248//                     for _ in 0..2 {
1249//                         let mut i = current_call_id;
1250//                         loop {
1251//                             new_hints.push(lsp::InlayHint {
1252//                                 position: lsp::Position::new(0, i),
1253//                                 label: lsp::InlayHintLabel::String(i.to_string()),
1254//                                 kind: None,
1255//                                 text_edits: None,
1256//                                 tooltip: None,
1257//                                 padding_left: None,
1258//                                 padding_right: None,
1259//                                 data: None,
1260//                             });
1261//                             if i == 0 {
1262//                                 break;
1263//                             }
1264//                             i -= 1;
1265//                         }
1266//                     }
1267
1268//                     Ok(Some(new_hints))
1269//                 }
1270//             })
1271//             .next()
1272//             .await;
1273//         cx.foreground().run_until_parked();
1274
1275//         let mut edits_made = 1;
1276//         editor.update(cx, |editor, cx| {
1277//             let expected_hints = vec!["0".to_string()];
1278//             assert_eq!(
1279//                 expected_hints,
1280//                 cached_hint_labels(editor),
1281//                 "Should get its first hints when opening the editor"
1282//             );
1283//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1284//             let inlay_cache = editor.inlay_hint_cache();
1285//             assert_eq!(
1286//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1287//                 "Cache should use editor settings to get the allowed hint kinds"
1288//             );
1289//             assert_eq!(
1290//                 inlay_cache.version, edits_made,
1291//                 "The editor update the cache version after every cache/view change"
1292//             );
1293//         });
1294
1295//         editor.update(cx, |editor, cx| {
1296//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1297//             editor.handle_input("some change", cx);
1298//             edits_made += 1;
1299//         });
1300//         cx.foreground().run_until_parked();
1301//         editor.update(cx, |editor, cx| {
1302//             let expected_hints = vec!["0".to_string(), "1".to_string()];
1303//             assert_eq!(
1304//                 expected_hints,
1305//                 cached_hint_labels(editor),
1306//                 "Should get new hints after an edit"
1307//             );
1308//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1309//             let inlay_cache = editor.inlay_hint_cache();
1310//             assert_eq!(
1311//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1312//                 "Cache should use editor settings to get the allowed hint kinds"
1313//             );
1314//             assert_eq!(
1315//                 inlay_cache.version, edits_made,
1316//                 "The editor update the cache version after every cache/view change"
1317//             );
1318//         });
1319
1320//         fake_server
1321//             .request::<lsp::request::InlayHintRefreshRequest>(())
1322//             .await
1323//             .expect("inlay refresh request failed");
1324//         edits_made += 1;
1325//         cx.foreground().run_until_parked();
1326//         editor.update(cx, |editor, cx| {
1327//             let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
1328//             assert_eq!(
1329//                 expected_hints,
1330//                 cached_hint_labels(editor),
1331//                 "Should get new hints after hint refresh/ request"
1332//             );
1333//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1334//             let inlay_cache = editor.inlay_hint_cache();
1335//             assert_eq!(
1336//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1337//                 "Cache should use editor settings to get the allowed hint kinds"
1338//             );
1339//             assert_eq!(
1340//                 inlay_cache.version, edits_made,
1341//                 "The editor update the cache version after every cache/view change"
1342//             );
1343//         });
1344//     }
1345
1346//     #[gpui::test]
1347//     async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
1348//         init_test(cx, |settings| {
1349//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1350//                 enabled: true,
1351//                 show_type_hints: true,
1352//                 show_parameter_hints: true,
1353//                 show_other_hints: true,
1354//             })
1355//         });
1356
1357//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1358//         let lsp_request_count = Arc::new(AtomicU32::new(0));
1359//         fake_server
1360//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1361//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
1362//                 async move {
1363//                     assert_eq!(
1364//                         params.text_document.uri,
1365//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
1366//                     );
1367//                     let current_call_id =
1368//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1369//                     Ok(Some(vec![lsp::InlayHint {
1370//                         position: lsp::Position::new(0, current_call_id),
1371//                         label: lsp::InlayHintLabel::String(current_call_id.to_string()),
1372//                         kind: None,
1373//                         text_edits: None,
1374//                         tooltip: None,
1375//                         padding_left: None,
1376//                         padding_right: None,
1377//                         data: None,
1378//                     }]))
1379//                 }
1380//             })
1381//             .next()
1382//             .await;
1383//         cx.foreground().run_until_parked();
1384
1385//         let mut edits_made = 1;
1386//         editor.update(cx, |editor, cx| {
1387//             let expected_hints = vec!["0".to_string()];
1388//             assert_eq!(
1389//                 expected_hints,
1390//                 cached_hint_labels(editor),
1391//                 "Should get its first hints when opening the editor"
1392//             );
1393//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1394//             assert_eq!(
1395//                 editor.inlay_hint_cache().version,
1396//                 edits_made,
1397//                 "The editor update the cache version after every cache/view change"
1398//             );
1399//         });
1400
1401//         let progress_token = "test_progress_token";
1402//         fake_server
1403//             .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
1404//                 token: lsp::ProgressToken::String(progress_token.to_string()),
1405//             })
1406//             .await
1407//             .expect("work done progress create request failed");
1408//         cx.foreground().run_until_parked();
1409//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
1410//             token: lsp::ProgressToken::String(progress_token.to_string()),
1411//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
1412//                 lsp::WorkDoneProgressBegin::default(),
1413//             )),
1414//         });
1415//         cx.foreground().run_until_parked();
1416
1417//         editor.update(cx, |editor, cx| {
1418//             let expected_hints = vec!["0".to_string()];
1419//             assert_eq!(
1420//                 expected_hints,
1421//                 cached_hint_labels(editor),
1422//                 "Should not update hints while the work task is running"
1423//             );
1424//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1425//             assert_eq!(
1426//                 editor.inlay_hint_cache().version,
1427//                 edits_made,
1428//                 "Should not update the cache while the work task is running"
1429//             );
1430//         });
1431
1432//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
1433//             token: lsp::ProgressToken::String(progress_token.to_string()),
1434//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
1435//                 lsp::WorkDoneProgressEnd::default(),
1436//             )),
1437//         });
1438//         cx.foreground().run_until_parked();
1439
1440//         edits_made += 1;
1441//         editor.update(cx, |editor, cx| {
1442//             let expected_hints = vec!["1".to_string()];
1443//             assert_eq!(
1444//                 expected_hints,
1445//                 cached_hint_labels(editor),
1446//                 "New hints should be queried after the work task is done"
1447//             );
1448//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1449//             assert_eq!(
1450//                 editor.inlay_hint_cache().version,
1451//                 edits_made,
1452//                 "Cache version should udpate once after the work task is done"
1453//             );
1454//         });
1455//     }
1456
1457//     #[gpui::test]
1458//     async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
1459//         init_test(cx, |settings| {
1460//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1461//                 enabled: true,
1462//                 show_type_hints: true,
1463//                 show_parameter_hints: true,
1464//                 show_other_hints: true,
1465//             })
1466//         });
1467
1468//         let fs = FakeFs::new(cx.background());
1469//         fs.insert_tree(
1470//                     "/a",
1471//                     json!({
1472//                         "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
1473//                         "other.md": "Test md file with some text",
1474//                     }),
1475//                 )
1476//                 .await;
1477//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
1478//         let workspace = cx
1479//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
1480//             .root(cx);
1481//         let worktree_id = workspace.update(cx, |workspace, cx| {
1482//             workspace.project().read_with(cx, |project, cx| {
1483//                 project.worktrees(cx).next().unwrap().read(cx).id()
1484//             })
1485//         });
1486
1487//         let mut rs_fake_servers = None;
1488//         let mut md_fake_servers = None;
1489//         for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
1490//             let mut language = Language::new(
1491//                 LanguageConfig {
1492//                     name: name.into(),
1493//                     path_suffixes: vec![path_suffix.to_string()],
1494//                     ..Default::default()
1495//                 },
1496//                 Some(tree_sitter_rust::language()),
1497//             );
1498//             let fake_servers = language
1499//                 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1500//                     name,
1501//                     capabilities: lsp::ServerCapabilities {
1502//                         inlay_hint_provider: Some(lsp::OneOf::Left(true)),
1503//                         ..Default::default()
1504//                     },
1505//                     ..Default::default()
1506//                 }))
1507//                 .await;
1508//             match name {
1509//                 "Rust" => rs_fake_servers = Some(fake_servers),
1510//                 "Markdown" => md_fake_servers = Some(fake_servers),
1511//                 _ => unreachable!(),
1512//             }
1513//             project.update(cx, |project, _| {
1514//                 project.languages().add(Arc::new(language));
1515//             });
1516//         }
1517
1518//         let _rs_buffer = project
1519//             .update(cx, |project, cx| {
1520//                 project.open_local_buffer("/a/main.rs", cx)
1521//             })
1522//             .await
1523//             .unwrap();
1524//         cx.foreground().run_until_parked();
1525//         cx.foreground().start_waiting();
1526//         let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
1527//         let rs_editor = workspace
1528//             .update(cx, |workspace, cx| {
1529//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
1530//             })
1531//             .await
1532//             .unwrap()
1533//             .downcast::<Editor>()
1534//             .unwrap();
1535//         let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
1536//         rs_fake_server
1537//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1538//                 let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
1539//                 async move {
1540//                     assert_eq!(
1541//                         params.text_document.uri,
1542//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
1543//                     );
1544//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1545//                     Ok(Some(vec![lsp::InlayHint {
1546//                         position: lsp::Position::new(0, i),
1547//                         label: lsp::InlayHintLabel::String(i.to_string()),
1548//                         kind: None,
1549//                         text_edits: None,
1550//                         tooltip: None,
1551//                         padding_left: None,
1552//                         padding_right: None,
1553//                         data: None,
1554//                     }]))
1555//                 }
1556//             })
1557//             .next()
1558//             .await;
1559//         cx.foreground().run_until_parked();
1560//         rs_editor.update(cx, |editor, cx| {
1561//             let expected_hints = vec!["0".to_string()];
1562//             assert_eq!(
1563//                 expected_hints,
1564//                 cached_hint_labels(editor),
1565//                 "Should get its first hints when opening the editor"
1566//             );
1567//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1568//             assert_eq!(
1569//                 editor.inlay_hint_cache().version,
1570//                 1,
1571//                 "Rust editor update the cache version after every cache/view change"
1572//             );
1573//         });
1574
1575//         cx.foreground().run_until_parked();
1576//         let _md_buffer = project
1577//             .update(cx, |project, cx| {
1578//                 project.open_local_buffer("/a/other.md", cx)
1579//             })
1580//             .await
1581//             .unwrap();
1582//         cx.foreground().run_until_parked();
1583//         cx.foreground().start_waiting();
1584//         let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
1585//         let md_editor = workspace
1586//             .update(cx, |workspace, cx| {
1587//                 workspace.open_path((worktree_id, "other.md"), None, true, cx)
1588//             })
1589//             .await
1590//             .unwrap()
1591//             .downcast::<Editor>()
1592//             .unwrap();
1593//         let md_lsp_request_count = Arc::new(AtomicU32::new(0));
1594//         md_fake_server
1595//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1596//                 let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
1597//                 async move {
1598//                     assert_eq!(
1599//                         params.text_document.uri,
1600//                         lsp::Url::from_file_path("/a/other.md").unwrap(),
1601//                     );
1602//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1603//                     Ok(Some(vec![lsp::InlayHint {
1604//                         position: lsp::Position::new(0, i),
1605//                         label: lsp::InlayHintLabel::String(i.to_string()),
1606//                         kind: None,
1607//                         text_edits: None,
1608//                         tooltip: None,
1609//                         padding_left: None,
1610//                         padding_right: None,
1611//                         data: None,
1612//                     }]))
1613//                 }
1614//             })
1615//             .next()
1616//             .await;
1617//         cx.foreground().run_until_parked();
1618//         md_editor.update(cx, |editor, cx| {
1619//             let expected_hints = vec!["0".to_string()];
1620//             assert_eq!(
1621//                 expected_hints,
1622//                 cached_hint_labels(editor),
1623//                 "Markdown editor should have a separate verison, repeating Rust editor rules"
1624//             );
1625//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1626//             assert_eq!(editor.inlay_hint_cache().version, 1);
1627//         });
1628
1629//         rs_editor.update(cx, |editor, cx| {
1630//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1631//             editor.handle_input("some rs change", cx);
1632//         });
1633//         cx.foreground().run_until_parked();
1634//         rs_editor.update(cx, |editor, cx| {
1635//             let expected_hints = vec!["1".to_string()];
1636//             assert_eq!(
1637//                 expected_hints,
1638//                 cached_hint_labels(editor),
1639//                 "Rust inlay cache should change after the edit"
1640//             );
1641//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1642//             assert_eq!(
1643//                 editor.inlay_hint_cache().version,
1644//                 2,
1645//                 "Every time hint cache changes, cache version should be incremented"
1646//             );
1647//         });
1648//         md_editor.update(cx, |editor, cx| {
1649//             let expected_hints = vec!["0".to_string()];
1650//             assert_eq!(
1651//                 expected_hints,
1652//                 cached_hint_labels(editor),
1653//                 "Markdown editor should not be affected by Rust editor changes"
1654//             );
1655//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1656//             assert_eq!(editor.inlay_hint_cache().version, 1);
1657//         });
1658
1659//         md_editor.update(cx, |editor, cx| {
1660//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1661//             editor.handle_input("some md change", cx);
1662//         });
1663//         cx.foreground().run_until_parked();
1664//         md_editor.update(cx, |editor, cx| {
1665//             let expected_hints = vec!["1".to_string()];
1666//             assert_eq!(
1667//                 expected_hints,
1668//                 cached_hint_labels(editor),
1669//                 "Rust editor should not be affected by Markdown editor changes"
1670//             );
1671//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1672//             assert_eq!(editor.inlay_hint_cache().version, 2);
1673//         });
1674//         rs_editor.update(cx, |editor, cx| {
1675//             let expected_hints = vec!["1".to_string()];
1676//             assert_eq!(
1677//                 expected_hints,
1678//                 cached_hint_labels(editor),
1679//                 "Markdown editor should also change independently"
1680//             );
1681//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1682//             assert_eq!(editor.inlay_hint_cache().version, 2);
1683//         });
1684//     }
1685
1686//     #[gpui::test]
1687//     async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
1688//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
1689//         init_test(cx, |settings| {
1690//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1691//                 enabled: true,
1692//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1693//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
1694//                 show_other_hints: allowed_hint_kinds.contains(&None),
1695//             })
1696//         });
1697
1698//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1699//         let lsp_request_count = Arc::new(AtomicU32::new(0));
1700//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
1701//         fake_server
1702//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1703//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
1704//                 async move {
1705//                     Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1706//                     assert_eq!(
1707//                         params.text_document.uri,
1708//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
1709//                     );
1710//                     Ok(Some(vec![
1711//                         lsp::InlayHint {
1712//                             position: lsp::Position::new(0, 1),
1713//                             label: lsp::InlayHintLabel::String("type hint".to_string()),
1714//                             kind: Some(lsp::InlayHintKind::TYPE),
1715//                             text_edits: None,
1716//                             tooltip: None,
1717//                             padding_left: None,
1718//                             padding_right: None,
1719//                             data: None,
1720//                         },
1721//                         lsp::InlayHint {
1722//                             position: lsp::Position::new(0, 2),
1723//                             label: lsp::InlayHintLabel::String("parameter hint".to_string()),
1724//                             kind: Some(lsp::InlayHintKind::PARAMETER),
1725//                             text_edits: None,
1726//                             tooltip: None,
1727//                             padding_left: None,
1728//                             padding_right: None,
1729//                             data: None,
1730//                         },
1731//                         lsp::InlayHint {
1732//                             position: lsp::Position::new(0, 3),
1733//                             label: lsp::InlayHintLabel::String("other hint".to_string()),
1734//                             kind: None,
1735//                             text_edits: None,
1736//                             tooltip: None,
1737//                             padding_left: None,
1738//                             padding_right: None,
1739//                             data: None,
1740//                         },
1741//                     ]))
1742//                 }
1743//             })
1744//             .next()
1745//             .await;
1746//         cx.foreground().run_until_parked();
1747
1748//         let mut edits_made = 1;
1749//         editor.update(cx, |editor, cx| {
1750//             assert_eq!(
1751//                 lsp_request_count.load(Ordering::Relaxed),
1752//                 1,
1753//                 "Should query new hints once"
1754//             );
1755//             assert_eq!(
1756//                 vec![
1757//                     "other hint".to_string(),
1758//                     "parameter hint".to_string(),
1759//                     "type hint".to_string(),
1760//                 ],
1761//                 cached_hint_labels(editor),
1762//                 "Should get its first hints when opening the editor"
1763//             );
1764//             assert_eq!(
1765//                 vec!["other hint".to_string(), "type hint".to_string()],
1766//                 visible_hint_labels(editor, cx)
1767//             );
1768//             let inlay_cache = editor.inlay_hint_cache();
1769//             assert_eq!(
1770//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1771//                 "Cache should use editor settings to get the allowed hint kinds"
1772//             );
1773//             assert_eq!(
1774//                 inlay_cache.version, edits_made,
1775//                 "The editor update the cache version after every cache/view change"
1776//             );
1777//         });
1778
1779//         fake_server
1780//             .request::<lsp::request::InlayHintRefreshRequest>(())
1781//             .await
1782//             .expect("inlay refresh request failed");
1783//         cx.foreground().run_until_parked();
1784//         editor.update(cx, |editor, cx| {
1785//             assert_eq!(
1786//                 lsp_request_count.load(Ordering::Relaxed),
1787//                 2,
1788//                 "Should load new hints twice"
1789//             );
1790//             assert_eq!(
1791//                 vec![
1792//                     "other hint".to_string(),
1793//                     "parameter hint".to_string(),
1794//                     "type hint".to_string(),
1795//                 ],
1796//                 cached_hint_labels(editor),
1797//                 "Cached hints should not change due to allowed hint kinds settings update"
1798//             );
1799//             assert_eq!(
1800//                 vec!["other hint".to_string(), "type hint".to_string()],
1801//                 visible_hint_labels(editor, cx)
1802//             );
1803//             assert_eq!(
1804//                 editor.inlay_hint_cache().version,
1805//                 edits_made,
1806//                 "Should not update cache version due to new loaded hints being the same"
1807//             );
1808//         });
1809
1810//         for (new_allowed_hint_kinds, expected_visible_hints) in [
1811//             (HashSet::from_iter([None]), vec!["other hint".to_string()]),
1812//             (
1813//                 HashSet::from_iter([Some(InlayHintKind::Type)]),
1814//                 vec!["type hint".to_string()],
1815//             ),
1816//             (
1817//                 HashSet::from_iter([Some(InlayHintKind::Parameter)]),
1818//                 vec!["parameter hint".to_string()],
1819//             ),
1820//             (
1821//                 HashSet::from_iter([None, Some(InlayHintKind::Type)]),
1822//                 vec!["other hint".to_string(), "type hint".to_string()],
1823//             ),
1824//             (
1825//                 HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
1826//                 vec!["other hint".to_string(), "parameter hint".to_string()],
1827//             ),
1828//             (
1829//                 HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
1830//                 vec!["parameter hint".to_string(), "type hint".to_string()],
1831//             ),
1832//             (
1833//                 HashSet::from_iter([
1834//                     None,
1835//                     Some(InlayHintKind::Type),
1836//                     Some(InlayHintKind::Parameter),
1837//                 ]),
1838//                 vec![
1839//                     "other hint".to_string(),
1840//                     "parameter hint".to_string(),
1841//                     "type hint".to_string(),
1842//                 ],
1843//             ),
1844//         ] {
1845//             edits_made += 1;
1846//             update_test_language_settings(cx, |settings| {
1847//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
1848//                     enabled: true,
1849//                     show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1850//                     show_parameter_hints: new_allowed_hint_kinds
1851//                         .contains(&Some(InlayHintKind::Parameter)),
1852//                     show_other_hints: new_allowed_hint_kinds.contains(&None),
1853//                 })
1854//             });
1855//             cx.foreground().run_until_parked();
1856//             editor.update(cx, |editor, cx| {
1857//                 assert_eq!(
1858//                     lsp_request_count.load(Ordering::Relaxed),
1859//                     2,
1860//                     "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
1861//                 );
1862//                 assert_eq!(
1863//                     vec![
1864//                         "other hint".to_string(),
1865//                         "parameter hint".to_string(),
1866//                         "type hint".to_string(),
1867//                     ],
1868//                     cached_hint_labels(editor),
1869//                     "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
1870//                 );
1871//                 assert_eq!(
1872//                     expected_visible_hints,
1873//                     visible_hint_labels(editor, cx),
1874//                     "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
1875//                 );
1876//                 let inlay_cache = editor.inlay_hint_cache();
1877//                 assert_eq!(
1878//                     inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
1879//                     "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
1880//                 );
1881//                 assert_eq!(
1882//                     inlay_cache.version, edits_made,
1883//                     "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
1884//                 );
1885//             });
1886//         }
1887
1888//         edits_made += 1;
1889//         let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
1890//         update_test_language_settings(cx, |settings| {
1891//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1892//                 enabled: false,
1893//                 show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1894//                 show_parameter_hints: another_allowed_hint_kinds
1895//                     .contains(&Some(InlayHintKind::Parameter)),
1896//                 show_other_hints: another_allowed_hint_kinds.contains(&None),
1897//             })
1898//         });
1899//         cx.foreground().run_until_parked();
1900//         editor.update(cx, |editor, cx| {
1901//             assert_eq!(
1902//                 lsp_request_count.load(Ordering::Relaxed),
1903//                 2,
1904//                 "Should not load new hints when hints got disabled"
1905//             );
1906//             assert!(
1907//                 cached_hint_labels(editor).is_empty(),
1908//                 "Should clear the cache when hints got disabled"
1909//             );
1910//             assert!(
1911//                 visible_hint_labels(editor, cx).is_empty(),
1912//                 "Should clear visible hints when hints got disabled"
1913//             );
1914//             let inlay_cache = editor.inlay_hint_cache();
1915//             assert_eq!(
1916//                 inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
1917//                 "Should update its allowed hint kinds even when hints got disabled"
1918//             );
1919//             assert_eq!(
1920//                 inlay_cache.version, edits_made,
1921//                 "The editor should update the cache version after hints got disabled"
1922//             );
1923//         });
1924
1925//         fake_server
1926//             .request::<lsp::request::InlayHintRefreshRequest>(())
1927//             .await
1928//             .expect("inlay refresh request failed");
1929//         cx.foreground().run_until_parked();
1930//         editor.update(cx, |editor, cx| {
1931//             assert_eq!(
1932//                 lsp_request_count.load(Ordering::Relaxed),
1933//                 2,
1934//                 "Should not load new hints when they got disabled"
1935//             );
1936//             assert!(cached_hint_labels(editor).is_empty());
1937//             assert!(visible_hint_labels(editor, cx).is_empty());
1938//             assert_eq!(
1939//                 editor.inlay_hint_cache().version, edits_made,
1940//                 "The editor should not update the cache version after /refresh query without updates"
1941//             );
1942//         });
1943
1944//         let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
1945//         edits_made += 1;
1946//         update_test_language_settings(cx, |settings| {
1947//             settings.defaults.inlay_hints = Some(InlayHintSettings {
1948//                 enabled: true,
1949//                 show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1950//                 show_parameter_hints: final_allowed_hint_kinds
1951//                     .contains(&Some(InlayHintKind::Parameter)),
1952//                 show_other_hints: final_allowed_hint_kinds.contains(&None),
1953//             })
1954//         });
1955//         cx.foreground().run_until_parked();
1956//         editor.update(cx, |editor, cx| {
1957//             assert_eq!(
1958//                 lsp_request_count.load(Ordering::Relaxed),
1959//                 3,
1960//                 "Should query for new hints when they got reenabled"
1961//             );
1962//             assert_eq!(
1963//                 vec![
1964//                     "other hint".to_string(),
1965//                     "parameter hint".to_string(),
1966//                     "type hint".to_string(),
1967//                 ],
1968//                 cached_hint_labels(editor),
1969//                 "Should get its cached hints fully repopulated after the hints got reenabled"
1970//             );
1971//             assert_eq!(
1972//                 vec!["parameter hint".to_string()],
1973//                 visible_hint_labels(editor, cx),
1974//                 "Should get its visible hints repopulated and filtered after the h"
1975//             );
1976//             let inlay_cache = editor.inlay_hint_cache();
1977//             assert_eq!(
1978//                 inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
1979//                 "Cache should update editor settings when hints got reenabled"
1980//             );
1981//             assert_eq!(
1982//                 inlay_cache.version, edits_made,
1983//                 "Cache should update its version after hints got reenabled"
1984//             );
1985//         });
1986
1987//         fake_server
1988//             .request::<lsp::request::InlayHintRefreshRequest>(())
1989//             .await
1990//             .expect("inlay refresh request failed");
1991//         cx.foreground().run_until_parked();
1992//         editor.update(cx, |editor, cx| {
1993//             assert_eq!(
1994//                 lsp_request_count.load(Ordering::Relaxed),
1995//                 4,
1996//                 "Should query for new hints again"
1997//             );
1998//             assert_eq!(
1999//                 vec![
2000//                     "other hint".to_string(),
2001//                     "parameter hint".to_string(),
2002//                     "type hint".to_string(),
2003//                 ],
2004//                 cached_hint_labels(editor),
2005//             );
2006//             assert_eq!(
2007//                 vec!["parameter hint".to_string()],
2008//                 visible_hint_labels(editor, cx),
2009//             );
2010//             assert_eq!(editor.inlay_hint_cache().version, edits_made);
2011//         });
2012//     }
2013
2014//     #[gpui::test]
2015//     async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
2016//         init_test(cx, |settings| {
2017//             settings.defaults.inlay_hints = Some(InlayHintSettings {
2018//                 enabled: true,
2019//                 show_type_hints: true,
2020//                 show_parameter_hints: true,
2021//                 show_other_hints: true,
2022//             })
2023//         });
2024
2025//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
2026//         let fake_server = Arc::new(fake_server);
2027//         let lsp_request_count = Arc::new(AtomicU32::new(0));
2028//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
2029//         fake_server
2030//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2031//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
2032//                 async move {
2033//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
2034//                     assert_eq!(
2035//                         params.text_document.uri,
2036//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
2037//                     );
2038//                     Ok(Some(vec![lsp::InlayHint {
2039//                         position: lsp::Position::new(0, i),
2040//                         label: lsp::InlayHintLabel::String(i.to_string()),
2041//                         kind: None,
2042//                         text_edits: None,
2043//                         tooltip: None,
2044//                         padding_left: None,
2045//                         padding_right: None,
2046//                         data: None,
2047//                     }]))
2048//                 }
2049//             })
2050//             .next()
2051//             .await;
2052
2053//         let mut expected_changes = Vec::new();
2054//         for change_after_opening in [
2055//             "initial change #1",
2056//             "initial change #2",
2057//             "initial change #3",
2058//         ] {
2059//             editor.update(cx, |editor, cx| {
2060//                 editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
2061//                 editor.handle_input(change_after_opening, cx);
2062//             });
2063//             expected_changes.push(change_after_opening);
2064//         }
2065
2066//         cx.foreground().run_until_parked();
2067
2068//         editor.update(cx, |editor, cx| {
2069//             let current_text = editor.text(cx);
2070//             for change in &expected_changes {
2071//                 assert!(
2072//                     current_text.contains(change),
2073//                     "Should apply all changes made"
2074//                 );
2075//             }
2076//             assert_eq!(
2077//                 lsp_request_count.load(Ordering::Relaxed),
2078//                 2,
2079//                 "Should query new hints twice: for editor init and for the last edit that interrupted all others"
2080//             );
2081//             let expected_hints = vec!["2".to_string()];
2082//             assert_eq!(
2083//                 expected_hints,
2084//                 cached_hint_labels(editor),
2085//                 "Should get hints from the last edit landed only"
2086//             );
2087//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2088//             assert_eq!(
2089//                 editor.inlay_hint_cache().version, 1,
2090//                 "Only one update should be registered in the cache after all cancellations"
2091//             );
2092//         });
2093
2094//         let mut edits = Vec::new();
2095//         for async_later_change in [
2096//             "another change #1",
2097//             "another change #2",
2098//             "another change #3",
2099//         ] {
2100//             expected_changes.push(async_later_change);
2101//             let task_editor = editor.clone();
2102//             let mut task_cx = cx.clone();
2103//             edits.push(cx.foreground().spawn(async move {
2104//                 task_editor.update(&mut task_cx, |editor, cx| {
2105//                     editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
2106//                     editor.handle_input(async_later_change, cx);
2107//                 });
2108//             }));
2109//         }
2110//         let _ = future::join_all(edits).await;
2111//         cx.foreground().run_until_parked();
2112
2113//         editor.update(cx, |editor, cx| {
2114//             let current_text = editor.text(cx);
2115//             for change in &expected_changes {
2116//                 assert!(
2117//                     current_text.contains(change),
2118//                     "Should apply all changes made"
2119//                 );
2120//             }
2121//             assert_eq!(
2122//                 lsp_request_count.load(Ordering::SeqCst),
2123//                 3,
2124//                 "Should query new hints one more time, for the last edit only"
2125//             );
2126//             let expected_hints = vec!["3".to_string()];
2127//             assert_eq!(
2128//                 expected_hints,
2129//                 cached_hint_labels(editor),
2130//                 "Should get hints from the last edit landed only"
2131//             );
2132//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2133//             assert_eq!(
2134//                 editor.inlay_hint_cache().version,
2135//                 2,
2136//                 "Should update the cache version once more, for the new change"
2137//             );
2138//         });
2139//     }
2140
2141//     #[gpui::test(iterations = 10)]
2142//     async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
2143//         init_test(cx, |settings| {
2144//             settings.defaults.inlay_hints = Some(InlayHintSettings {
2145//                 enabled: true,
2146//                 show_type_hints: true,
2147//                 show_parameter_hints: true,
2148//                 show_other_hints: true,
2149//             })
2150//         });
2151
2152//         let mut language = Language::new(
2153//             LanguageConfig {
2154//                 name: "Rust".into(),
2155//                 path_suffixes: vec!["rs".to_string()],
2156//                 ..Default::default()
2157//             },
2158//             Some(tree_sitter_rust::language()),
2159//         );
2160//         let mut fake_servers = language
2161//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2162//                 capabilities: lsp::ServerCapabilities {
2163//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2164//                     ..Default::default()
2165//                 },
2166//                 ..Default::default()
2167//             }))
2168//             .await;
2169//         let fs = FakeFs::new(cx.background());
2170//         fs.insert_tree(
2171//             "/a",
2172//             json!({
2173//                 "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
2174//                 "other.rs": "// Test file",
2175//             }),
2176//         )
2177//         .await;
2178//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
2179//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
2180//         let workspace = cx
2181//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
2182//             .root(cx);
2183//         let worktree_id = workspace.update(cx, |workspace, cx| {
2184//             workspace.project().read_with(cx, |project, cx| {
2185//                 project.worktrees(cx).next().unwrap().read(cx).id()
2186//             })
2187//         });
2188
2189//         let _buffer = project
2190//             .update(cx, |project, cx| {
2191//                 project.open_local_buffer("/a/main.rs", cx)
2192//             })
2193//             .await
2194//             .unwrap();
2195//         cx.foreground().run_until_parked();
2196//         cx.foreground().start_waiting();
2197//         let fake_server = fake_servers.next().await.unwrap();
2198//         let editor = workspace
2199//             .update(cx, |workspace, cx| {
2200//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
2201//             })
2202//             .await
2203//             .unwrap()
2204//             .downcast::<Editor>()
2205//             .unwrap();
2206//         let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
2207//         let lsp_request_count = Arc::new(AtomicUsize::new(0));
2208//         let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
2209//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
2210//         fake_server
2211//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2212//                 let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
2213//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
2214//                 async move {
2215//                     assert_eq!(
2216//                         params.text_document.uri,
2217//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
2218//                     );
2219
2220//                     task_lsp_request_ranges.lock().push(params.range);
2221//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
2222//                     Ok(Some(vec![lsp::InlayHint {
2223//                         position: params.range.end,
2224//                         label: lsp::InlayHintLabel::String(i.to_string()),
2225//                         kind: None,
2226//                         text_edits: None,
2227//                         tooltip: None,
2228//                         padding_left: None,
2229//                         padding_right: None,
2230//                         data: None,
2231//                     }]))
2232//                 }
2233//             })
2234//             .next()
2235//             .await;
2236//         fn editor_visible_range(
2237//             editor: &ViewHandle<Editor>,
2238//             cx: &mut gpui::TestAppContext,
2239//         ) -> Range<Point> {
2240//             let ranges = editor.update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx));
2241//             assert_eq!(
2242//                 ranges.len(),
2243//                 1,
2244//                 "Single buffer should produce a single excerpt with visible range"
2245//             );
2246//             let (_, (excerpt_buffer, _, excerpt_visible_range)) =
2247//                 ranges.into_iter().next().unwrap();
2248//             excerpt_buffer.update(cx, |buffer, _| {
2249//                 let snapshot = buffer.snapshot();
2250//                 let start = buffer
2251//                     .anchor_before(excerpt_visible_range.start)
2252//                     .to_point(&snapshot);
2253//                 let end = buffer
2254//                     .anchor_after(excerpt_visible_range.end)
2255//                     .to_point(&snapshot);
2256//                 start..end
2257//             })
2258//         }
2259
2260//         // in large buffers, requests are made for more than visible range of a buffer.
2261//         // invisible parts are queried later, to avoid excessive requests on quick typing.
2262//         // wait the timeout needed to get all requests.
2263//         cx.foreground().advance_clock(Duration::from_millis(
2264//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2265//         ));
2266//         cx.foreground().run_until_parked();
2267//         let initial_visible_range = editor_visible_range(&editor, cx);
2268//         let lsp_initial_visible_range = lsp::Range::new(
2269//             lsp::Position::new(
2270//                 initial_visible_range.start.row,
2271//                 initial_visible_range.start.column,
2272//             ),
2273//             lsp::Position::new(
2274//                 initial_visible_range.end.row,
2275//                 initial_visible_range.end.column,
2276//             ),
2277//         );
2278//         let expected_initial_query_range_end =
2279//             lsp::Position::new(initial_visible_range.end.row * 2, 2);
2280//         let mut expected_invisible_query_start = lsp_initial_visible_range.end;
2281//         expected_invisible_query_start.character += 1;
2282//         editor.update(cx, |editor, cx| {
2283//             let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
2284//             assert_eq!(ranges.len(), 2,
2285//                 "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
2286//             let visible_query_range = &ranges[0];
2287//             assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
2288//             assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
2289//             let invisible_query_range = &ranges[1];
2290
2291//             assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
2292//             assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
2293
2294//             let requests_count = lsp_request_count.load(Ordering::Acquire);
2295//             assert_eq!(requests_count, 2, "Visible + invisible request");
2296//             let expected_hints = vec!["1".to_string(), "2".to_string()];
2297//             assert_eq!(
2298//                 expected_hints,
2299//                 cached_hint_labels(editor),
2300//                 "Should have hints from both LSP requests made for a big file"
2301//             );
2302//             assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
2303//             assert_eq!(
2304//                 editor.inlay_hint_cache().version, requests_count,
2305//                 "LSP queries should've bumped the cache version"
2306//             );
2307//         });
2308
2309//         editor.update(cx, |editor, cx| {
2310//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
2311//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
2312//         });
2313//         cx.foreground().advance_clock(Duration::from_millis(
2314//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2315//         ));
2316//         cx.foreground().run_until_parked();
2317//         let visible_range_after_scrolls = editor_visible_range(&editor, cx);
2318//         let visible_line_count =
2319//             editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
2320//         let selection_in_cached_range = editor.update(cx, |editor, cx| {
2321//             let ranges = lsp_request_ranges
2322//                 .lock()
2323//                 .drain(..)
2324//                 .sorted_by_key(|r| r.start)
2325//                 .collect::<Vec<_>>();
2326//             assert_eq!(
2327//                 ranges.len(),
2328//                 2,
2329//                 "Should query 2 ranges after both scrolls, but got: {ranges:?}"
2330//             );
2331//             let first_scroll = &ranges[0];
2332//             let second_scroll = &ranges[1];
2333//             assert_eq!(
2334//                 first_scroll.end, second_scroll.start,
2335//                 "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
2336//             );
2337//             assert_eq!(
2338//                 first_scroll.start, expected_initial_query_range_end,
2339//                 "First scroll should start the query right after the end of the original scroll",
2340//             );
2341//             assert_eq!(
2342//                 second_scroll.end,
2343//                 lsp::Position::new(
2344//                     visible_range_after_scrolls.end.row
2345//                         + visible_line_count.ceil() as u32,
2346//                     1,
2347//                 ),
2348//                 "Second scroll should query one more screen down after the end of the visible range"
2349//             );
2350
2351//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
2352//             assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
2353//             let expected_hints = vec![
2354//                 "1".to_string(),
2355//                 "2".to_string(),
2356//                 "3".to_string(),
2357//                 "4".to_string(),
2358//             ];
2359//             assert_eq!(
2360//                 expected_hints,
2361//                 cached_hint_labels(editor),
2362//                 "Should have hints from the new LSP response after the edit"
2363//             );
2364//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2365//             assert_eq!(
2366//                 editor.inlay_hint_cache().version,
2367//                 lsp_requests,
2368//                 "Should update the cache for every LSP response with hints added"
2369//             );
2370
2371//             let mut selection_in_cached_range = visible_range_after_scrolls.end;
2372//             selection_in_cached_range.row -= visible_line_count.ceil() as u32;
2373//             selection_in_cached_range
2374//         });
2375
2376//         editor.update(cx, |editor, cx| {
2377//             editor.change_selections(Some(Autoscroll::center()), cx, |s| {
2378//                 s.select_ranges([selection_in_cached_range..selection_in_cached_range])
2379//             });
2380//         });
2381//         cx.foreground().advance_clock(Duration::from_millis(
2382//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2383//         ));
2384//         cx.foreground().run_until_parked();
2385//         editor.update(cx, |_, _| {
2386//             let ranges = lsp_request_ranges
2387//                 .lock()
2388//                 .drain(..)
2389//                 .sorted_by_key(|r| r.start)
2390//                 .collect::<Vec<_>>();
2391//             assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
2392//             assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
2393//         });
2394
2395//         editor.update(cx, |editor, cx| {
2396//             editor.handle_input("++++more text++++", cx);
2397//         });
2398//         cx.foreground().advance_clock(Duration::from_millis(
2399//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2400//         ));
2401//         cx.foreground().run_until_parked();
2402//         editor.update(cx, |editor, cx| {
2403//             let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
2404//             ranges.sort_by_key(|r| r.start);
2405
2406//             assert_eq!(ranges.len(), 3,
2407//                 "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
2408//             let above_query_range = &ranges[0];
2409//             let visible_query_range = &ranges[1];
2410//             let below_query_range = &ranges[2];
2411//             assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
2412//                 "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
2413//             assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
2414//                 "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
2415//             assert!(above_query_range.start.line < selection_in_cached_range.row,
2416//                 "Hints should be queried with the selected range after the query range start");
2417//             assert!(below_query_range.end.line > selection_in_cached_range.row,
2418//                 "Hints should be queried with the selected range before the query range end");
2419//             assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
2420//                 "Hints query range should contain one more screen before");
2421//             assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
2422//                 "Hints query range should contain one more screen after");
2423
2424//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
2425//             assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
2426//             let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
2427//             assert_eq!(expected_hints, cached_hint_labels(editor),
2428//                 "Should have hints from the new LSP response after the edit");
2429//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2430//             assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
2431//         });
2432//     }
2433
2434//     #[gpui::test(iterations = 10)]
2435//     async fn test_multiple_excerpts_large_multibuffer(
2436//         deterministic: Arc<Deterministic>,
2437//         cx: &mut gpui::TestAppContext,
2438//     ) {
2439//         init_test(cx, |settings| {
2440//             settings.defaults.inlay_hints = Some(InlayHintSettings {
2441//                 enabled: true,
2442//                 show_type_hints: true,
2443//                 show_parameter_hints: true,
2444//                 show_other_hints: true,
2445//             })
2446//         });
2447
2448//         let mut language = Language::new(
2449//             LanguageConfig {
2450//                 name: "Rust".into(),
2451//                 path_suffixes: vec!["rs".to_string()],
2452//                 ..Default::default()
2453//             },
2454//             Some(tree_sitter_rust::language()),
2455//         );
2456//         let mut fake_servers = language
2457//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2458//                 capabilities: lsp::ServerCapabilities {
2459//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2460//                     ..Default::default()
2461//                 },
2462//                 ..Default::default()
2463//             }))
2464//             .await;
2465//         let language = Arc::new(language);
2466//         let fs = FakeFs::new(cx.background());
2467//         fs.insert_tree(
2468//             "/a",
2469//             json!({
2470//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
2471//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
2472//             }),
2473//         )
2474//         .await;
2475//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
2476//         project.update(cx, |project, _| {
2477//             project.languages().add(Arc::clone(&language))
2478//         });
2479//         let workspace = cx
2480//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
2481//             .root(cx);
2482//         let worktree_id = workspace.update(cx, |workspace, cx| {
2483//             workspace.project().read_with(cx, |project, cx| {
2484//                 project.worktrees(cx).next().unwrap().read(cx).id()
2485//             })
2486//         });
2487
2488//         let buffer_1 = project
2489//             .update(cx, |project, cx| {
2490//                 project.open_buffer((worktree_id, "main.rs"), cx)
2491//             })
2492//             .await
2493//             .unwrap();
2494//         let buffer_2 = project
2495//             .update(cx, |project, cx| {
2496//                 project.open_buffer((worktree_id, "other.rs"), cx)
2497//             })
2498//             .await
2499//             .unwrap();
2500//         let multibuffer = cx.add_model(|cx| {
2501//             let mut multibuffer = MultiBuffer::new(0);
2502//             multibuffer.push_excerpts(
2503//                 buffer_1.clone(),
2504//                 [
2505//                     ExcerptRange {
2506//                         context: Point::new(0, 0)..Point::new(2, 0),
2507//                         primary: None,
2508//                     },
2509//                     ExcerptRange {
2510//                         context: Point::new(4, 0)..Point::new(11, 0),
2511//                         primary: None,
2512//                     },
2513//                     ExcerptRange {
2514//                         context: Point::new(22, 0)..Point::new(33, 0),
2515//                         primary: None,
2516//                     },
2517//                     ExcerptRange {
2518//                         context: Point::new(44, 0)..Point::new(55, 0),
2519//                         primary: None,
2520//                     },
2521//                     ExcerptRange {
2522//                         context: Point::new(56, 0)..Point::new(66, 0),
2523//                         primary: None,
2524//                     },
2525//                     ExcerptRange {
2526//                         context: Point::new(67, 0)..Point::new(77, 0),
2527//                         primary: None,
2528//                     },
2529//                 ],
2530//                 cx,
2531//             );
2532//             multibuffer.push_excerpts(
2533//                 buffer_2.clone(),
2534//                 [
2535//                     ExcerptRange {
2536//                         context: Point::new(0, 1)..Point::new(2, 1),
2537//                         primary: None,
2538//                     },
2539//                     ExcerptRange {
2540//                         context: Point::new(4, 1)..Point::new(11, 1),
2541//                         primary: None,
2542//                     },
2543//                     ExcerptRange {
2544//                         context: Point::new(22, 1)..Point::new(33, 1),
2545//                         primary: None,
2546//                     },
2547//                     ExcerptRange {
2548//                         context: Point::new(44, 1)..Point::new(55, 1),
2549//                         primary: None,
2550//                     },
2551//                     ExcerptRange {
2552//                         context: Point::new(56, 1)..Point::new(66, 1),
2553//                         primary: None,
2554//                     },
2555//                     ExcerptRange {
2556//                         context: Point::new(67, 1)..Point::new(77, 1),
2557//                         primary: None,
2558//                     },
2559//                 ],
2560//                 cx,
2561//             );
2562//             multibuffer
2563//         });
2564
2565//         deterministic.run_until_parked();
2566//         cx.foreground().run_until_parked();
2567//         let editor = cx
2568//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
2569//             .root(cx);
2570//         let editor_edited = Arc::new(AtomicBool::new(false));
2571//         let fake_server = fake_servers.next().await.unwrap();
2572//         let closure_editor_edited = Arc::clone(&editor_edited);
2573//         fake_server
2574//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2575//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
2576//                 async move {
2577//                     let hint_text = if params.text_document.uri
2578//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
2579//                     {
2580//                         "main hint"
2581//                     } else if params.text_document.uri
2582//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
2583//                     {
2584//                         "other hint"
2585//                     } else {
2586//                         panic!("unexpected uri: {:?}", params.text_document.uri);
2587//                     };
2588
2589//                     // one hint per excerpt
2590//                     let positions = [
2591//                         lsp::Position::new(0, 2),
2592//                         lsp::Position::new(4, 2),
2593//                         lsp::Position::new(22, 2),
2594//                         lsp::Position::new(44, 2),
2595//                         lsp::Position::new(56, 2),
2596//                         lsp::Position::new(67, 2),
2597//                     ];
2598//                     let out_of_range_hint = lsp::InlayHint {
2599//                         position: lsp::Position::new(
2600//                             params.range.start.line + 99,
2601//                             params.range.start.character + 99,
2602//                         ),
2603//                         label: lsp::InlayHintLabel::String(
2604//                             "out of excerpt range, should be ignored".to_string(),
2605//                         ),
2606//                         kind: None,
2607//                         text_edits: None,
2608//                         tooltip: None,
2609//                         padding_left: None,
2610//                         padding_right: None,
2611//                         data: None,
2612//                     };
2613
2614//                     let edited = task_editor_edited.load(Ordering::Acquire);
2615//                     Ok(Some(
2616//                         std::iter::once(out_of_range_hint)
2617//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
2618//                                 lsp::InlayHint {
2619//                                     position,
2620//                                     label: lsp::InlayHintLabel::String(format!(
2621//                                         "{hint_text}{} #{i}",
2622//                                         if edited { "(edited)" } else { "" },
2623//                                     )),
2624//                                     kind: None,
2625//                                     text_edits: None,
2626//                                     tooltip: None,
2627//                                     padding_left: None,
2628//                                     padding_right: None,
2629//                                     data: None,
2630//                                 }
2631//                             }))
2632//                             .collect(),
2633//                     ))
2634//                 }
2635//             })
2636//             .next()
2637//             .await;
2638//         cx.foreground().run_until_parked();
2639
2640//         editor.update(cx, |editor, cx| {
2641//             let expected_hints = vec![
2642//                 "main hint #0".to_string(),
2643//                 "main hint #1".to_string(),
2644//                 "main hint #2".to_string(),
2645//                 "main hint #3".to_string(),
2646//             ];
2647//             assert_eq!(
2648//                 expected_hints,
2649//                 cached_hint_labels(editor),
2650//                 "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
2651//             );
2652//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2653//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
2654//         });
2655
2656//         editor.update(cx, |editor, cx| {
2657//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2658//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
2659//             });
2660//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2661//                 s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
2662//             });
2663//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2664//                 s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
2665//             });
2666//         });
2667//         cx.foreground().run_until_parked();
2668//         editor.update(cx, |editor, cx| {
2669//             let expected_hints = vec![
2670//                 "main hint #0".to_string(),
2671//                 "main hint #1".to_string(),
2672//                 "main hint #2".to_string(),
2673//                 "main hint #3".to_string(),
2674//                 "main hint #4".to_string(),
2675//                 "main hint #5".to_string(),
2676//                 "other hint #0".to_string(),
2677//                 "other hint #1".to_string(),
2678//                 "other hint #2".to_string(),
2679//             ];
2680//             assert_eq!(expected_hints, cached_hint_labels(editor),
2681//                 "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
2682//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2683//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
2684//                 "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
2685//         });
2686
2687//         editor.update(cx, |editor, cx| {
2688//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2689//                 s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
2690//             });
2691//         });
2692//         cx.foreground().advance_clock(Duration::from_millis(
2693//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2694//         ));
2695//         cx.foreground().run_until_parked();
2696//         let last_scroll_update_version = editor.update(cx, |editor, cx| {
2697//             let expected_hints = vec![
2698//                 "main hint #0".to_string(),
2699//                 "main hint #1".to_string(),
2700//                 "main hint #2".to_string(),
2701//                 "main hint #3".to_string(),
2702//                 "main hint #4".to_string(),
2703//                 "main hint #5".to_string(),
2704//                 "other hint #0".to_string(),
2705//                 "other hint #1".to_string(),
2706//                 "other hint #2".to_string(),
2707//                 "other hint #3".to_string(),
2708//                 "other hint #4".to_string(),
2709//                 "other hint #5".to_string(),
2710//             ];
2711//             assert_eq!(expected_hints, cached_hint_labels(editor),
2712//                 "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
2713//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2714//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
2715//             expected_hints.len()
2716//         });
2717
2718//         editor.update(cx, |editor, cx| {
2719//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2720//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
2721//             });
2722//         });
2723//         cx.foreground().run_until_parked();
2724//         editor.update(cx, |editor, cx| {
2725//             let expected_hints = vec![
2726//                 "main hint #0".to_string(),
2727//                 "main hint #1".to_string(),
2728//                 "main hint #2".to_string(),
2729//                 "main hint #3".to_string(),
2730//                 "main hint #4".to_string(),
2731//                 "main hint #5".to_string(),
2732//                 "other hint #0".to_string(),
2733//                 "other hint #1".to_string(),
2734//                 "other hint #2".to_string(),
2735//                 "other hint #3".to_string(),
2736//                 "other hint #4".to_string(),
2737//                 "other hint #5".to_string(),
2738//             ];
2739//             assert_eq!(expected_hints, cached_hint_labels(editor),
2740//                 "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
2741//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2742//             assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
2743//         });
2744
2745//         editor_edited.store(true, Ordering::Release);
2746//         editor.update(cx, |editor, cx| {
2747//             editor.change_selections(None, cx, |s| {
2748//                 s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
2749//             });
2750//             editor.handle_input("++++more text++++", cx);
2751//         });
2752//         cx.foreground().run_until_parked();
2753//         editor.update(cx, |editor, cx| {
2754//             let expected_hints = vec![
2755//                 "main hint(edited) #0".to_string(),
2756//                 "main hint(edited) #1".to_string(),
2757//                 "main hint(edited) #2".to_string(),
2758//                 "main hint(edited) #3".to_string(),
2759//                 "main hint(edited) #4".to_string(),
2760//                 "main hint(edited) #5".to_string(),
2761//                 "other hint(edited) #0".to_string(),
2762//                 "other hint(edited) #1".to_string(),
2763//             ];
2764//             assert_eq!(
2765//                 expected_hints,
2766//                 cached_hint_labels(editor),
2767//                 "After multibuffer edit, editor gets scolled back to the last selection; \
2768// all hints should be invalidated and requeried for all of its visible excerpts"
2769//             );
2770//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2771
2772//             let current_cache_version = editor.inlay_hint_cache().version;
2773//             let minimum_expected_version = last_scroll_update_version + expected_hints.len();
2774//             assert!(
2775//                 current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
2776//                 "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
2777//             );
2778//         });
2779//     }
2780
2781//     #[gpui::test]
2782//     async fn test_excerpts_removed(
2783//         deterministic: Arc<Deterministic>,
2784//         cx: &mut gpui::TestAppContext,
2785//     ) {
2786//         init_test(cx, |settings| {
2787//             settings.defaults.inlay_hints = Some(InlayHintSettings {
2788//                 enabled: true,
2789//                 show_type_hints: false,
2790//                 show_parameter_hints: false,
2791//                 show_other_hints: false,
2792//             })
2793//         });
2794
2795//         let mut language = Language::new(
2796//             LanguageConfig {
2797//                 name: "Rust".into(),
2798//                 path_suffixes: vec!["rs".to_string()],
2799//                 ..Default::default()
2800//             },
2801//             Some(tree_sitter_rust::language()),
2802//         );
2803//         let mut fake_servers = language
2804//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2805//                 capabilities: lsp::ServerCapabilities {
2806//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2807//                     ..Default::default()
2808//                 },
2809//                 ..Default::default()
2810//             }))
2811//             .await;
2812//         let language = Arc::new(language);
2813//         let fs = FakeFs::new(cx.background());
2814//         fs.insert_tree(
2815//             "/a",
2816//             json!({
2817//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
2818//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
2819//             }),
2820//         )
2821//         .await;
2822//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
2823//         project.update(cx, |project, _| {
2824//             project.languages().add(Arc::clone(&language))
2825//         });
2826//         let workspace = cx
2827//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
2828//             .root(cx);
2829//         let worktree_id = workspace.update(cx, |workspace, cx| {
2830//             workspace.project().read_with(cx, |project, cx| {
2831//                 project.worktrees(cx).next().unwrap().read(cx).id()
2832//             })
2833//         });
2834
2835//         let buffer_1 = project
2836//             .update(cx, |project, cx| {
2837//                 project.open_buffer((worktree_id, "main.rs"), cx)
2838//             })
2839//             .await
2840//             .unwrap();
2841//         let buffer_2 = project
2842//             .update(cx, |project, cx| {
2843//                 project.open_buffer((worktree_id, "other.rs"), cx)
2844//             })
2845//             .await
2846//             .unwrap();
2847//         let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2848//         let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
2849//             let buffer_1_excerpts = multibuffer.push_excerpts(
2850//                 buffer_1.clone(),
2851//                 [ExcerptRange {
2852//                     context: Point::new(0, 0)..Point::new(2, 0),
2853//                     primary: None,
2854//                 }],
2855//                 cx,
2856//             );
2857//             let buffer_2_excerpts = multibuffer.push_excerpts(
2858//                 buffer_2.clone(),
2859//                 [ExcerptRange {
2860//                     context: Point::new(0, 1)..Point::new(2, 1),
2861//                     primary: None,
2862//                 }],
2863//                 cx,
2864//             );
2865//             (buffer_1_excerpts, buffer_2_excerpts)
2866//         });
2867
2868//         assert!(!buffer_1_excerpts.is_empty());
2869//         assert!(!buffer_2_excerpts.is_empty());
2870
2871//         deterministic.run_until_parked();
2872//         cx.foreground().run_until_parked();
2873//         let editor = cx
2874//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
2875//             .root(cx);
2876//         let editor_edited = Arc::new(AtomicBool::new(false));
2877//         let fake_server = fake_servers.next().await.unwrap();
2878//         let closure_editor_edited = Arc::clone(&editor_edited);
2879//         fake_server
2880//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2881//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
2882//                 async move {
2883//                     let hint_text = if params.text_document.uri
2884//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
2885//                     {
2886//                         "main hint"
2887//                     } else if params.text_document.uri
2888//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
2889//                     {
2890//                         "other hint"
2891//                     } else {
2892//                         panic!("unexpected uri: {:?}", params.text_document.uri);
2893//                     };
2894
2895//                     let positions = [
2896//                         lsp::Position::new(0, 2),
2897//                         lsp::Position::new(4, 2),
2898//                         lsp::Position::new(22, 2),
2899//                         lsp::Position::new(44, 2),
2900//                         lsp::Position::new(56, 2),
2901//                         lsp::Position::new(67, 2),
2902//                     ];
2903//                     let out_of_range_hint = lsp::InlayHint {
2904//                         position: lsp::Position::new(
2905//                             params.range.start.line + 99,
2906//                             params.range.start.character + 99,
2907//                         ),
2908//                         label: lsp::InlayHintLabel::String(
2909//                             "out of excerpt range, should be ignored".to_string(),
2910//                         ),
2911//                         kind: None,
2912//                         text_edits: None,
2913//                         tooltip: None,
2914//                         padding_left: None,
2915//                         padding_right: None,
2916//                         data: None,
2917//                     };
2918
2919//                     let edited = task_editor_edited.load(Ordering::Acquire);
2920//                     Ok(Some(
2921//                         std::iter::once(out_of_range_hint)
2922//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
2923//                                 lsp::InlayHint {
2924//                                     position,
2925//                                     label: lsp::InlayHintLabel::String(format!(
2926//                                         "{hint_text}{} #{i}",
2927//                                         if edited { "(edited)" } else { "" },
2928//                                     )),
2929//                                     kind: None,
2930//                                     text_edits: None,
2931//                                     tooltip: None,
2932//                                     padding_left: None,
2933//                                     padding_right: None,
2934//                                     data: None,
2935//                                 }
2936//                             }))
2937//                             .collect(),
2938//                     ))
2939//                 }
2940//             })
2941//             .next()
2942//             .await;
2943//         cx.foreground().run_until_parked();
2944
2945//         editor.update(cx, |editor, cx| {
2946//             assert_eq!(
2947//                 vec!["main hint #0".to_string(), "other hint #0".to_string()],
2948//                 cached_hint_labels(editor),
2949//                 "Cache should update for both excerpts despite hints display was disabled"
2950//             );
2951//             assert!(
2952//                 visible_hint_labels(editor, cx).is_empty(),
2953//                 "All hints are disabled and should not be shown despite being present in the cache"
2954//             );
2955//             assert_eq!(
2956//                 editor.inlay_hint_cache().version,
2957//                 2,
2958//                 "Cache should update once per excerpt query"
2959//             );
2960//         });
2961
2962//         editor.update(cx, |editor, cx| {
2963//             editor.buffer().update(cx, |multibuffer, cx| {
2964//                 multibuffer.remove_excerpts(buffer_2_excerpts, cx)
2965//             })
2966//         });
2967//         cx.foreground().run_until_parked();
2968//         editor.update(cx, |editor, cx| {
2969//             assert_eq!(
2970//                 vec!["main hint #0".to_string()],
2971//                 cached_hint_labels(editor),
2972//                 "For the removed excerpt, should clean corresponding cached hints"
2973//             );
2974//             assert!(
2975//                 visible_hint_labels(editor, cx).is_empty(),
2976//                 "All hints are disabled and should not be shown despite being present in the cache"
2977//             );
2978//             assert_eq!(
2979//                 editor.inlay_hint_cache().version,
2980//                 3,
2981//                 "Excerpt removal should trigger a cache update"
2982//             );
2983//         });
2984
2985//         update_test_language_settings(cx, |settings| {
2986//             settings.defaults.inlay_hints = Some(InlayHintSettings {
2987//                 enabled: true,
2988//                 show_type_hints: true,
2989//                 show_parameter_hints: true,
2990//                 show_other_hints: true,
2991//             })
2992//         });
2993//         cx.foreground().run_until_parked();
2994//         editor.update(cx, |editor, cx| {
2995//             let expected_hints = vec!["main hint #0".to_string()];
2996//             assert_eq!(
2997//                 expected_hints,
2998//                 cached_hint_labels(editor),
2999//                 "Hint display settings change should not change the cache"
3000//             );
3001//             assert_eq!(
3002//                 expected_hints,
3003//                 visible_hint_labels(editor, cx),
3004//                 "Settings change should make cached hints visible"
3005//             );
3006//             assert_eq!(
3007//                 editor.inlay_hint_cache().version,
3008//                 4,
3009//                 "Settings change should trigger a cache update"
3010//             );
3011//         });
3012//     }
3013
3014//     #[gpui::test]
3015//     async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
3016//         init_test(cx, |settings| {
3017//             settings.defaults.inlay_hints = Some(InlayHintSettings {
3018//                 enabled: true,
3019//                 show_type_hints: true,
3020//                 show_parameter_hints: true,
3021//                 show_other_hints: true,
3022//             })
3023//         });
3024
3025//         let mut language = Language::new(
3026//             LanguageConfig {
3027//                 name: "Rust".into(),
3028//                 path_suffixes: vec!["rs".to_string()],
3029//                 ..Default::default()
3030//             },
3031//             Some(tree_sitter_rust::language()),
3032//         );
3033//         let mut fake_servers = language
3034//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3035//                 capabilities: lsp::ServerCapabilities {
3036//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
3037//                     ..Default::default()
3038//                 },
3039//                 ..Default::default()
3040//             }))
3041//             .await;
3042//         let fs = FakeFs::new(cx.background());
3043//         fs.insert_tree(
3044//             "/a",
3045//             json!({
3046//                 "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
3047//                 "other.rs": "// Test file",
3048//             }),
3049//         )
3050//         .await;
3051//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
3052//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3053//         let workspace = cx
3054//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
3055//             .root(cx);
3056//         let worktree_id = workspace.update(cx, |workspace, cx| {
3057//             workspace.project().read_with(cx, |project, cx| {
3058//                 project.worktrees(cx).next().unwrap().read(cx).id()
3059//             })
3060//         });
3061
3062//         let _buffer = project
3063//             .update(cx, |project, cx| {
3064//                 project.open_local_buffer("/a/main.rs", cx)
3065//             })
3066//             .await
3067//             .unwrap();
3068//         cx.foreground().run_until_parked();
3069//         cx.foreground().start_waiting();
3070//         let fake_server = fake_servers.next().await.unwrap();
3071//         let editor = workspace
3072//             .update(cx, |workspace, cx| {
3073//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
3074//             })
3075//             .await
3076//             .unwrap()
3077//             .downcast::<Editor>()
3078//             .unwrap();
3079//         let lsp_request_count = Arc::new(AtomicU32::new(0));
3080//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
3081//         fake_server
3082//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
3083//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
3084//                 async move {
3085//                     assert_eq!(
3086//                         params.text_document.uri,
3087//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
3088//                     );
3089//                     let query_start = params.range.start;
3090//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
3091//                     Ok(Some(vec![lsp::InlayHint {
3092//                         position: query_start,
3093//                         label: lsp::InlayHintLabel::String(i.to_string()),
3094//                         kind: None,
3095//                         text_edits: None,
3096//                         tooltip: None,
3097//                         padding_left: None,
3098//                         padding_right: None,
3099//                         data: None,
3100//                     }]))
3101//                 }
3102//             })
3103//             .next()
3104//             .await;
3105
3106//         cx.foreground().run_until_parked();
3107//         editor.update(cx, |editor, cx| {
3108//             editor.change_selections(None, cx, |s| {
3109//                 s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
3110//             })
3111//         });
3112//         cx.foreground().run_until_parked();
3113//         editor.update(cx, |editor, cx| {
3114//             let expected_hints = vec!["1".to_string()];
3115//             assert_eq!(expected_hints, cached_hint_labels(editor));
3116//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3117//             assert_eq!(editor.inlay_hint_cache().version, 1);
3118//         });
3119//     }
3120
3121//     #[gpui::test]
3122//     async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
3123//         init_test(cx, |settings| {
3124//             settings.defaults.inlay_hints = Some(InlayHintSettings {
3125//                 enabled: false,
3126//                 show_type_hints: true,
3127//                 show_parameter_hints: true,
3128//                 show_other_hints: true,
3129//             })
3130//         });
3131
3132//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
3133
3134//         editor.update(cx, |editor, cx| {
3135//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3136//         });
3137//         cx.foreground().start_waiting();
3138//         let lsp_request_count = Arc::new(AtomicU32::new(0));
3139//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
3140//         fake_server
3141//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
3142//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
3143//                 async move {
3144//                     assert_eq!(
3145//                         params.text_document.uri,
3146//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
3147//                     );
3148
3149//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
3150//                     Ok(Some(vec![lsp::InlayHint {
3151//                         position: lsp::Position::new(0, i),
3152//                         label: lsp::InlayHintLabel::String(i.to_string()),
3153//                         kind: None,
3154//                         text_edits: None,
3155//                         tooltip: None,
3156//                         padding_left: None,
3157//                         padding_right: None,
3158//                         data: None,
3159//                     }]))
3160//                 }
3161//             })
3162//             .next()
3163//             .await;
3164//         cx.foreground().run_until_parked();
3165//         editor.update(cx, |editor, cx| {
3166//             let expected_hints = vec!["1".to_string()];
3167//             assert_eq!(
3168//                 expected_hints,
3169//                 cached_hint_labels(editor),
3170//                 "Should display inlays after toggle despite them disabled in settings"
3171//             );
3172//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3173//             assert_eq!(
3174//                 editor.inlay_hint_cache().version,
3175//                 1,
3176//                 "First toggle should be cache's first update"
3177//             );
3178//         });
3179
3180//         editor.update(cx, |editor, cx| {
3181//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3182//         });
3183//         cx.foreground().run_until_parked();
3184//         editor.update(cx, |editor, cx| {
3185//             assert!(
3186//                 cached_hint_labels(editor).is_empty(),
3187//                 "Should clear hints after 2nd toggle"
3188//             );
3189//             assert!(visible_hint_labels(editor, cx).is_empty());
3190//             assert_eq!(editor.inlay_hint_cache().version, 2);
3191//         });
3192
3193//         update_test_language_settings(cx, |settings| {
3194//             settings.defaults.inlay_hints = Some(InlayHintSettings {
3195//                 enabled: true,
3196//                 show_type_hints: true,
3197//                 show_parameter_hints: true,
3198//                 show_other_hints: true,
3199//             })
3200//         });
3201//         cx.foreground().run_until_parked();
3202//         editor.update(cx, |editor, cx| {
3203//             let expected_hints = vec!["2".to_string()];
3204//             assert_eq!(
3205//                 expected_hints,
3206//                 cached_hint_labels(editor),
3207//                 "Should query LSP hints for the 2nd time after enabling hints in settings"
3208//             );
3209//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3210//             assert_eq!(editor.inlay_hint_cache().version, 3);
3211//         });
3212
3213//         editor.update(cx, |editor, cx| {
3214//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3215//         });
3216//         cx.foreground().run_until_parked();
3217//         editor.update(cx, |editor, cx| {
3218//             assert!(
3219//                 cached_hint_labels(editor).is_empty(),
3220//                 "Should clear hints after enabling in settings and a 3rd toggle"
3221//             );
3222//             assert!(visible_hint_labels(editor, cx).is_empty());
3223//             assert_eq!(editor.inlay_hint_cache().version, 4);
3224//         });
3225
3226//         editor.update(cx, |editor, cx| {
3227//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3228//         });
3229//         cx.foreground().run_until_parked();
3230//         editor.update(cx, |editor, cx| {
3231//             let expected_hints = vec!["3".to_string()];
3232//             assert_eq!(
3233//                 expected_hints,
3234//                 cached_hint_labels(editor),
3235//                 "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
3236//             );
3237//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3238//             assert_eq!(editor.inlay_hint_cache().version, 5);
3239//         });
3240//     }
3241
3242//     pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
3243//         cx.foreground().forbid_parking();
3244
3245//         cx.update(|cx| {
3246//             cx.set_global(SettingsStore::test(cx));
3247//             theme::init(cx);
3248//             client::init_settings(cx);
3249//             language::init(cx);
3250//             Project::init_settings(cx);
3251//             workspace::init_settings(cx);
3252//             crate::init(cx);
3253//         });
3254
3255//         update_test_language_settings(cx, f);
3256//     }
3257
3258//     async fn prepare_test_objects(
3259//         cx: &mut TestAppContext,
3260//     ) -> (&'static str, ViewHandle<Editor>, FakeLanguageServer) {
3261//         let mut language = Language::new(
3262//             LanguageConfig {
3263//                 name: "Rust".into(),
3264//                 path_suffixes: vec!["rs".to_string()],
3265//                 ..Default::default()
3266//             },
3267//             Some(tree_sitter_rust::language()),
3268//         );
3269//         let mut fake_servers = language
3270//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3271//                 capabilities: lsp::ServerCapabilities {
3272//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
3273//                     ..Default::default()
3274//                 },
3275//                 ..Default::default()
3276//             }))
3277//             .await;
3278
3279//         let fs = FakeFs::new(cx.background());
3280//         fs.insert_tree(
3281//             "/a",
3282//             json!({
3283//                 "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
3284//                 "other.rs": "// Test file",
3285//             }),
3286//         )
3287//         .await;
3288
3289//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
3290//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3291//         let workspace = cx
3292//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
3293//             .root(cx);
3294//         let worktree_id = workspace.update(cx, |workspace, cx| {
3295//             workspace.project().read_with(cx, |project, cx| {
3296//                 project.worktrees(cx).next().unwrap().read(cx).id()
3297//             })
3298//         });
3299
3300//         let _buffer = project
3301//             .update(cx, |project, cx| {
3302//                 project.open_local_buffer("/a/main.rs", cx)
3303//             })
3304//             .await
3305//             .unwrap();
3306//         cx.foreground().run_until_parked();
3307//         cx.foreground().start_waiting();
3308//         let fake_server = fake_servers.next().await.unwrap();
3309//         let editor = workspace
3310//             .update(cx, |workspace, cx| {
3311//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
3312//             })
3313//             .await
3314//             .unwrap()
3315//             .downcast::<Editor>()
3316//             .unwrap();
3317
3318//         editor.update(cx, |editor, cx| {
3319//             assert!(cached_hint_labels(editor).is_empty());
3320//             assert!(visible_hint_labels(editor, cx).is_empty());
3321//             assert_eq!(editor.inlay_hint_cache().version, 0);
3322//         });
3323
3324//         ("/a/main.rs", editor, fake_server)
3325//     }
3326
3327//     pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
3328//         let mut labels = Vec::new();
3329//         for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
3330//             let excerpt_hints = excerpt_hints.read();
3331//             for id in &excerpt_hints.ordered_hints {
3332//                 labels.push(excerpt_hints.hints_by_id[id].text());
3333//             }
3334//         }
3335
3336//         labels.sort();
3337//         labels
3338//     }
3339
3340//     pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, '_, Editor>) -> Vec<String> {
3341//         let mut hints = editor
3342//             .visible_inlay_hints(cx)
3343//             .into_iter()
3344//             .map(|hint| hint.text.to_string())
3345//             .collect::<Vec<_>>();
3346//         hints.sort();
3347//         hints
3348//     }
3349// }