Properly sanitize out inlay hints from remote hosts (#42878)

Kirill Bulatov created

Part of https://github.com/zed-industries/zed/issues/42671

Release Notes:

- Fixed remote hosts causing duplicate hints to be displayed

Change summary

crates/collab/src/tests/editor_tests.rs | 32 ++++++++++++++++++--------
crates/project/src/lsp_store.rs         | 11 +++++++++
2 files changed, 33 insertions(+), 10 deletions(-)

Detailed changes

crates/collab/src/tests/editor_tests.rs 🔗

@@ -2169,16 +2169,28 @@ async fn test_inlay_hint_refresh_is_forwarded(
                 } else {
                     "initial hint"
                 };
-                Ok(Some(vec![lsp::InlayHint {
-                    position: lsp::Position::new(0, character),
-                    label: lsp::InlayHintLabel::String(label.to_string()),
-                    kind: None,
-                    text_edits: None,
-                    tooltip: None,
-                    padding_left: None,
-                    padding_right: None,
-                    data: None,
-                }]))
+                Ok(Some(vec![
+                    lsp::InlayHint {
+                        position: lsp::Position::new(0, character),
+                        label: lsp::InlayHintLabel::String(label.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    },
+                    lsp::InlayHint {
+                        position: lsp::Position::new(1090, 1090),
+                        label: lsp::InlayHintLabel::String("out-of-bounds hint".to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    },
+                ]))
             }
         })
         .next()

crates/project/src/lsp_store.rs 🔗

@@ -6881,6 +6881,7 @@ impl LspStore {
                 }))
                 .await;
 
+                let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
                 let mut has_errors = false;
                 let inlay_hints = inlay_hints
                     .into_iter()
@@ -6892,6 +6893,16 @@ impl LspStore {
                             None
                         }
                     })
+                    .map(|(server_id, mut new_hints)| {
+                        new_hints.retain(|hint| {
+                            hint.position.is_valid(&buffer_snapshot)
+                                && range.start.is_valid(&buffer_snapshot)
+                                && range.end.is_valid(&buffer_snapshot)
+                                && hint.position.cmp(&range.start, &buffer_snapshot).is_ge()
+                                && hint.position.cmp(&range.end, &buffer_snapshot).is_lt()
+                        });
+                        (server_id, new_hints)
+                    })
                     .collect::<HashMap<_, _>>();
                 anyhow::ensure!(
                     !has_errors || !inlay_hints.is_empty(),