Rework editors to register and query buffers on scroll (#40388)

Kirill Bulatov created

Preparation to https://github.com/zed-industries/zed/pull/40183
Moves https://github.com/zed-industries/zed/pull/22958 further: now,
instead of selection, scrolling the buffer into view is enough to get
registered and, later, be queried for its LSP data such as inlay hints,
diagnostics and document colors.

This effectively undoes https://github.com/zed-industries/zed/pull/28855
as now we try to register whatever's visible more aggressively, instead
of implicitly via inlay hints.

Release Notes:

- Reworked editors to register and query buffers on scroll

Change summary

crates/collab/src/tests/editor_tests.rs         | 381 ++++++++++--------
crates/editor/src/editor.rs                     | 240 +++++------
crates/editor/src/inlay_hint_cache.rs           |  36 +
crates/editor/src/linked_editing_ranges.rs      |   2 
crates/editor/src/lsp_colors.rs                 |  32 
crates/editor/src/scroll.rs                     |  25 
crates/multi_buffer/src/multi_buffer.rs         |   9 
crates/multi_buffer/src/multi_buffer_tests.rs   |   3 
crates/project/src/lsp_store.rs                 |  77 +--
crates/project/src/manifest_tree/server_tree.rs |   2 
10 files changed, 421 insertions(+), 386 deletions(-)

Detailed changes

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

@@ -2553,6 +2553,27 @@ async fn test_lsp_pull_diagnostics(
     cx_a.update(editor::init);
     cx_b.update(editor::init);
 
+    let expected_push_diagnostic_main_message = "pushed main diagnostic";
+    let expected_push_diagnostic_lib_message = "pushed lib diagnostic";
+    let expected_pull_diagnostic_main_message = "pulled main diagnostic";
+    let expected_pull_diagnostic_lib_message = "pulled lib diagnostic";
+    let expected_workspace_pull_diagnostics_main_message = "pulled workspace main diagnostic";
+    let expected_workspace_pull_diagnostics_lib_message = "pulled workspace lib diagnostic";
+
+    let diagnostics_pulls_result_ids = Arc::new(Mutex::new(BTreeSet::<Option<String>>::new()));
+    let workspace_diagnostics_pulls_result_ids = Arc::new(Mutex::new(BTreeSet::<String>::new()));
+    let diagnostics_pulls_made = Arc::new(AtomicUsize::new(0));
+    let closure_diagnostics_pulls_made = diagnostics_pulls_made.clone();
+    let closure_diagnostics_pulls_result_ids = diagnostics_pulls_result_ids.clone();
+    let workspace_diagnostics_pulls_made = Arc::new(AtomicUsize::new(0));
+    let closure_workspace_diagnostics_pulls_made = workspace_diagnostics_pulls_made.clone();
+    let closure_workspace_diagnostics_pulls_result_ids =
+        workspace_diagnostics_pulls_result_ids.clone();
+    let (workspace_diagnostic_cancel_tx, closure_workspace_diagnostic_cancel_rx) =
+        smol::channel::bounded::<()>(1);
+    let (closure_workspace_diagnostic_received_tx, workspace_diagnostic_received_rx) =
+        smol::channel::bounded::<()>(1);
+
     let capabilities = lsp::ServerCapabilities {
         diagnostic_provider: Some(lsp::DiagnosticServerCapabilities::Options(
             lsp::DiagnosticOptions {
@@ -2567,13 +2588,195 @@ async fn test_lsp_pull_diagnostics(
         ..lsp::ServerCapabilities::default()
     };
     client_a.language_registry().add(rust_lang());
+
+    let pull_diagnostics_handle = Arc::new(parking_lot::Mutex::new(None));
+    let workspace_diagnostics_pulls_handle = Arc::new(parking_lot::Mutex::new(None));
+
+    let closure_pull_diagnostics_handle = pull_diagnostics_handle.clone();
+    let closure_workspace_diagnostics_pulls_handle = workspace_diagnostics_pulls_handle.clone();
     let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
         "Rust",
         FakeLspAdapter {
             capabilities: capabilities.clone(),
+            initializer: Some(Box::new(move |fake_language_server| {
+                let expected_workspace_diagnostic_token = lsp::ProgressToken::String(format!(
+                    "workspace/diagnostic-{}-1",
+                    fake_language_server.server.server_id()
+                ));
+                let closure_workspace_diagnostics_pulls_result_ids = closure_workspace_diagnostics_pulls_result_ids.clone();
+                let diagnostics_pulls_made = closure_diagnostics_pulls_made.clone();
+                let diagnostics_pulls_result_ids = closure_diagnostics_pulls_result_ids.clone();
+                let closure_pull_diagnostics_handle = closure_pull_diagnostics_handle.clone();
+                let closure_workspace_diagnostics_pulls_handle = closure_workspace_diagnostics_pulls_handle.clone();
+                let closure_workspace_diagnostic_cancel_rx = closure_workspace_diagnostic_cancel_rx.clone();
+                let closure_workspace_diagnostic_received_tx = closure_workspace_diagnostic_received_tx.clone();
+                let pull_diagnostics_handle = fake_language_server
+                    .set_request_handler::<lsp::request::DocumentDiagnosticRequest, _, _>(
+                        move |params, _| {
+                            let requests_made = diagnostics_pulls_made.clone();
+                            let diagnostics_pulls_result_ids =
+                                diagnostics_pulls_result_ids.clone();
+                            async move {
+                                let message = if lsp::Uri::from_file_path(path!("/a/main.rs"))
+                                    .unwrap()
+                                    == params.text_document.uri
+                                {
+                                    expected_pull_diagnostic_main_message.to_string()
+                                } else if lsp::Uri::from_file_path(path!("/a/lib.rs")).unwrap()
+                                    == params.text_document.uri
+                                {
+                                    expected_pull_diagnostic_lib_message.to_string()
+                                } else {
+                                    panic!("Unexpected document: {}", params.text_document.uri)
+                                };
+                                {
+                                    diagnostics_pulls_result_ids
+                                        .lock()
+                                        .await
+                                        .insert(params.previous_result_id);
+                                }
+                                let new_requests_count =
+                                    requests_made.fetch_add(1, atomic::Ordering::Release) + 1;
+                                Ok(lsp::DocumentDiagnosticReportResult::Report(
+                                    lsp::DocumentDiagnosticReport::Full(
+                                        lsp::RelatedFullDocumentDiagnosticReport {
+                                            related_documents: None,
+                                            full_document_diagnostic_report:
+                                                lsp::FullDocumentDiagnosticReport {
+                                                    result_id: Some(format!(
+                                                        "pull-{new_requests_count}"
+                                                    )),
+                                                    items: vec![lsp::Diagnostic {
+                                                        range: lsp::Range {
+                                                            start: lsp::Position {
+                                                                line: 0,
+                                                                character: 0,
+                                                            },
+                                                            end: lsp::Position {
+                                                                line: 0,
+                                                                character: 2,
+                                                            },
+                                                        },
+                                                        severity: Some(
+                                                            lsp::DiagnosticSeverity::ERROR,
+                                                        ),
+                                                        message,
+                                                        ..lsp::Diagnostic::default()
+                                                    }],
+                                                },
+                                        },
+                                    ),
+                                ))
+                            }
+                        },
+                    );
+                let _ = closure_pull_diagnostics_handle.lock().insert(pull_diagnostics_handle);
+
+                let closure_workspace_diagnostics_pulls_made = closure_workspace_diagnostics_pulls_made.clone();
+                let workspace_diagnostics_pulls_handle = fake_language_server.set_request_handler::<lsp::request::WorkspaceDiagnosticRequest, _, _>(
+                    move |params, _| {
+                        let workspace_requests_made = closure_workspace_diagnostics_pulls_made.clone();
+                        let workspace_diagnostics_pulls_result_ids =
+                            closure_workspace_diagnostics_pulls_result_ids.clone();
+                        let workspace_diagnostic_cancel_rx = closure_workspace_diagnostic_cancel_rx.clone();
+                        let workspace_diagnostic_received_tx = closure_workspace_diagnostic_received_tx.clone();
+                        let expected_workspace_diagnostic_token = expected_workspace_diagnostic_token.clone();
+                        async move {
+                            let workspace_request_count =
+                                workspace_requests_made.fetch_add(1, atomic::Ordering::Release) + 1;
+                            {
+                                workspace_diagnostics_pulls_result_ids
+                                    .lock()
+                                    .await
+                                    .extend(params.previous_result_ids.into_iter().map(|id| id.value));
+                            }
+                            if should_stream_workspace_diagnostic && !workspace_diagnostic_cancel_rx.is_closed()
+                            {
+                                assert_eq!(
+                                    params.partial_result_params.partial_result_token,
+                                    Some(expected_workspace_diagnostic_token)
+                                );
+                                workspace_diagnostic_received_tx.send(()).await.unwrap();
+                                workspace_diagnostic_cancel_rx.recv().await.unwrap();
+                                workspace_diagnostic_cancel_rx.close();
+                                // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#partialResults
+                                // > The final response has to be empty in terms of result values.
+                                return Ok(lsp::WorkspaceDiagnosticReportResult::Report(
+                                    lsp::WorkspaceDiagnosticReport { items: Vec::new() },
+                                ));
+                            }
+                            Ok(lsp::WorkspaceDiagnosticReportResult::Report(
+                                lsp::WorkspaceDiagnosticReport {
+                                    items: vec![
+                                        lsp::WorkspaceDocumentDiagnosticReport::Full(
+                                            lsp::WorkspaceFullDocumentDiagnosticReport {
+                                                uri: lsp::Uri::from_file_path(path!("/a/main.rs")).unwrap(),
+                                                version: None,
+                                                full_document_diagnostic_report:
+                                                    lsp::FullDocumentDiagnosticReport {
+                                                        result_id: Some(format!(
+                                                            "workspace_{workspace_request_count}"
+                                                        )),
+                                                        items: vec![lsp::Diagnostic {
+                                                            range: lsp::Range {
+                                                                start: lsp::Position {
+                                                                    line: 0,
+                                                                    character: 1,
+                                                                },
+                                                                end: lsp::Position {
+                                                                    line: 0,
+                                                                    character: 3,
+                                                                },
+                                                            },
+                                                            severity: Some(lsp::DiagnosticSeverity::WARNING),
+                                                            message:
+                                                                expected_workspace_pull_diagnostics_main_message
+                                                                    .to_string(),
+                                                            ..lsp::Diagnostic::default()
+                                                        }],
+                                                    },
+                                            },
+                                        ),
+                                        lsp::WorkspaceDocumentDiagnosticReport::Full(
+                                            lsp::WorkspaceFullDocumentDiagnosticReport {
+                                                uri: lsp::Uri::from_file_path(path!("/a/lib.rs")).unwrap(),
+                                                version: None,
+                                                full_document_diagnostic_report:
+                                                    lsp::FullDocumentDiagnosticReport {
+                                                        result_id: Some(format!(
+                                                            "workspace_{workspace_request_count}"
+                                                        )),
+                                                        items: vec![lsp::Diagnostic {
+                                                            range: lsp::Range {
+                                                                start: lsp::Position {
+                                                                    line: 0,
+                                                                    character: 1,
+                                                                },
+                                                                end: lsp::Position {
+                                                                    line: 0,
+                                                                    character: 3,
+                                                                },
+                                                            },
+                                                            severity: Some(lsp::DiagnosticSeverity::WARNING),
+                                                            message:
+                                                                expected_workspace_pull_diagnostics_lib_message
+                                                                    .to_string(),
+                                                            ..lsp::Diagnostic::default()
+                                                        }],
+                                                    },
+                                            },
+                                        ),
+                                    ],
+                                },
+                            ))
+                        }
+                    });
+                let _ = closure_workspace_diagnostics_pulls_handle.lock().insert(workspace_diagnostics_pulls_handle);
+            })),
             ..FakeLspAdapter::default()
         },
     );
+
     client_b.language_registry().add(rust_lang());
     client_b.language_registry().register_fake_lsp_adapter(
         "Rust",
@@ -2631,183 +2834,15 @@ async fn test_lsp_pull_diagnostics(
         .unwrap();
 
     let fake_language_server = fake_language_servers.next().await.unwrap();
-    cx_a.run_until_parked();
-    cx_b.run_until_parked();
-    let expected_push_diagnostic_main_message = "pushed main diagnostic";
-    let expected_push_diagnostic_lib_message = "pushed lib diagnostic";
-    let expected_pull_diagnostic_main_message = "pulled main diagnostic";
-    let expected_pull_diagnostic_lib_message = "pulled lib diagnostic";
-    let expected_workspace_pull_diagnostics_main_message = "pulled workspace main diagnostic";
-    let expected_workspace_pull_diagnostics_lib_message = "pulled workspace lib diagnostic";
-
-    let diagnostics_pulls_result_ids = Arc::new(Mutex::new(BTreeSet::<Option<String>>::new()));
-    let workspace_diagnostics_pulls_result_ids = Arc::new(Mutex::new(BTreeSet::<String>::new()));
-    let diagnostics_pulls_made = Arc::new(AtomicUsize::new(0));
-    let closure_diagnostics_pulls_made = diagnostics_pulls_made.clone();
-    let closure_diagnostics_pulls_result_ids = diagnostics_pulls_result_ids.clone();
-    let mut pull_diagnostics_handle = fake_language_server
-        .set_request_handler::<lsp::request::DocumentDiagnosticRequest, _, _>(move |params, _| {
-            let requests_made = closure_diagnostics_pulls_made.clone();
-            let diagnostics_pulls_result_ids = closure_diagnostics_pulls_result_ids.clone();
-            async move {
-                let message = if lsp::Uri::from_file_path(path!("/a/main.rs")).unwrap()
-                    == params.text_document.uri
-                {
-                    expected_pull_diagnostic_main_message.to_string()
-                } else if lsp::Uri::from_file_path(path!("/a/lib.rs")).unwrap()
-                    == params.text_document.uri
-                {
-                    expected_pull_diagnostic_lib_message.to_string()
-                } else {
-                    panic!("Unexpected document: {}", params.text_document.uri)
-                };
-                {
-                    diagnostics_pulls_result_ids
-                        .lock()
-                        .await
-                        .insert(params.previous_result_id);
-                }
-                let new_requests_count = requests_made.fetch_add(1, atomic::Ordering::Release) + 1;
-                Ok(lsp::DocumentDiagnosticReportResult::Report(
-                    lsp::DocumentDiagnosticReport::Full(lsp::RelatedFullDocumentDiagnosticReport {
-                        related_documents: None,
-                        full_document_diagnostic_report: lsp::FullDocumentDiagnosticReport {
-                            result_id: Some(format!("pull-{new_requests_count}")),
-                            items: vec![lsp::Diagnostic {
-                                range: lsp::Range {
-                                    start: lsp::Position {
-                                        line: 0,
-                                        character: 0,
-                                    },
-                                    end: lsp::Position {
-                                        line: 0,
-                                        character: 2,
-                                    },
-                                },
-                                severity: Some(lsp::DiagnosticSeverity::ERROR),
-                                message,
-                                ..lsp::Diagnostic::default()
-                            }],
-                        },
-                    }),
-                ))
-            }
-        });
-
-    let workspace_diagnostics_pulls_made = Arc::new(AtomicUsize::new(0));
-    let closure_workspace_diagnostics_pulls_made = workspace_diagnostics_pulls_made.clone();
-    let closure_workspace_diagnostics_pulls_result_ids =
-        workspace_diagnostics_pulls_result_ids.clone();
-    let (workspace_diagnostic_cancel_tx, closure_workspace_diagnostic_cancel_rx) =
-        smol::channel::bounded::<()>(1);
-    let (closure_workspace_diagnostic_received_tx, workspace_diagnostic_received_rx) =
-        smol::channel::bounded::<()>(1);
     let expected_workspace_diagnostic_token = lsp::ProgressToken::String(format!(
         "workspace/diagnostic-{}-1",
         fake_language_server.server.server_id()
     ));
-    let closure_expected_workspace_diagnostic_token = expected_workspace_diagnostic_token.clone();
-    let mut workspace_diagnostics_pulls_handle = fake_language_server
-        .set_request_handler::<lsp::request::WorkspaceDiagnosticRequest, _, _>(
-        move |params, _| {
-            let workspace_requests_made = closure_workspace_diagnostics_pulls_made.clone();
-            let workspace_diagnostics_pulls_result_ids =
-                closure_workspace_diagnostics_pulls_result_ids.clone();
-            let workspace_diagnostic_cancel_rx = closure_workspace_diagnostic_cancel_rx.clone();
-            let workspace_diagnostic_received_tx = closure_workspace_diagnostic_received_tx.clone();
-            let expected_workspace_diagnostic_token =
-                closure_expected_workspace_diagnostic_token.clone();
-            async move {
-                let workspace_request_count =
-                    workspace_requests_made.fetch_add(1, atomic::Ordering::Release) + 1;
-                {
-                    workspace_diagnostics_pulls_result_ids
-                        .lock()
-                        .await
-                        .extend(params.previous_result_ids.into_iter().map(|id| id.value));
-                }
-                if should_stream_workspace_diagnostic && !workspace_diagnostic_cancel_rx.is_closed()
-                {
-                    assert_eq!(
-                        params.partial_result_params.partial_result_token,
-                        Some(expected_workspace_diagnostic_token)
-                    );
-                    workspace_diagnostic_received_tx.send(()).await.unwrap();
-                    workspace_diagnostic_cancel_rx.recv().await.unwrap();
-                    workspace_diagnostic_cancel_rx.close();
-                    // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#partialResults
-                    // > The final response has to be empty in terms of result values.
-                    return Ok(lsp::WorkspaceDiagnosticReportResult::Report(
-                        lsp::WorkspaceDiagnosticReport { items: Vec::new() },
-                    ));
-                }
-                Ok(lsp::WorkspaceDiagnosticReportResult::Report(
-                    lsp::WorkspaceDiagnosticReport {
-                        items: vec![
-                            lsp::WorkspaceDocumentDiagnosticReport::Full(
-                                lsp::WorkspaceFullDocumentDiagnosticReport {
-                                    uri: lsp::Uri::from_file_path(path!("/a/main.rs")).unwrap(),
-                                    version: None,
-                                    full_document_diagnostic_report:
-                                        lsp::FullDocumentDiagnosticReport {
-                                            result_id: Some(format!(
-                                                "workspace_{workspace_request_count}"
-                                            )),
-                                            items: vec![lsp::Diagnostic {
-                                                range: lsp::Range {
-                                                    start: lsp::Position {
-                                                        line: 0,
-                                                        character: 1,
-                                                    },
-                                                    end: lsp::Position {
-                                                        line: 0,
-                                                        character: 3,
-                                                    },
-                                                },
-                                                severity: Some(lsp::DiagnosticSeverity::WARNING),
-                                                message:
-                                                    expected_workspace_pull_diagnostics_main_message
-                                                        .to_string(),
-                                                ..lsp::Diagnostic::default()
-                                            }],
-                                        },
-                                },
-                            ),
-                            lsp::WorkspaceDocumentDiagnosticReport::Full(
-                                lsp::WorkspaceFullDocumentDiagnosticReport {
-                                    uri: lsp::Uri::from_file_path(path!("/a/lib.rs")).unwrap(),
-                                    version: None,
-                                    full_document_diagnostic_report:
-                                        lsp::FullDocumentDiagnosticReport {
-                                            result_id: Some(format!(
-                                                "workspace_{workspace_request_count}"
-                                            )),
-                                            items: vec![lsp::Diagnostic {
-                                                range: lsp::Range {
-                                                    start: lsp::Position {
-                                                        line: 0,
-                                                        character: 1,
-                                                    },
-                                                    end: lsp::Position {
-                                                        line: 0,
-                                                        character: 3,
-                                                    },
-                                                },
-                                                severity: Some(lsp::DiagnosticSeverity::WARNING),
-                                                message:
-                                                    expected_workspace_pull_diagnostics_lib_message
-                                                        .to_string(),
-                                                ..lsp::Diagnostic::default()
-                                            }],
-                                        },
-                                },
-                            ),
-                        ],
-                    },
-                ))
-            }
-        },
-    );
+    cx_a.run_until_parked();
+    cx_b.run_until_parked();
+    let mut pull_diagnostics_handle = pull_diagnostics_handle.lock().take().unwrap();
+    let mut workspace_diagnostics_pulls_handle =
+        workspace_diagnostics_pulls_handle.lock().take().unwrap();
 
     if should_stream_workspace_diagnostic {
         workspace_diagnostic_received_rx.recv().await.unwrap();

crates/editor/src/editor.rs 🔗

@@ -1190,6 +1190,7 @@ pub struct Editor {
     inline_value_cache: InlineValueCache,
     selection_drag_state: SelectionDragState,
     colors: Option<LspColorData>,
+    post_scroll_update: Task<()>,
     refresh_colors_task: Task<()>,
     folding_newlines: Task<()>,
     pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
@@ -1786,7 +1787,7 @@ impl Editor {
 
     fn new_internal(
         mode: EditorMode,
-        buffer: Entity<MultiBuffer>,
+        multi_buffer: Entity<MultiBuffer>,
         project: Option<Entity<Project>>,
         display_map: Option<Entity<DisplayMap>>,
         window: &mut Window,
@@ -1844,7 +1845,7 @@ impl Editor {
         let display_map = display_map.unwrap_or_else(|| {
             cx.new(|cx| {
                 DisplayMap::new(
-                    buffer.clone(),
+                    multi_buffer.clone(),
                     style.font(),
                     font_size,
                     None,
@@ -1857,7 +1858,7 @@ impl Editor {
             })
         });
 
-        let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
+        let selections = SelectionsCollection::new(display_map.clone(), multi_buffer.clone());
 
         let blink_manager = cx.new(|cx| {
             let mut blink_manager = BlinkManager::new(CURSOR_BLINK_INTERVAL, cx);
@@ -1909,8 +1910,19 @@ impl Editor {
                         }
                     }
                     project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
-                        if editor.buffer().read(cx).buffer(*buffer_id).is_some() {
-                            editor.update_lsp_data(false, Some(*buffer_id), window, cx);
+                        let buffer_id = *buffer_id;
+                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
+                            let registered = editor.register_buffer(buffer_id, cx);
+                            if registered {
+                                editor.update_lsp_data(Some(buffer_id), window, cx);
+                                editor.refresh_inlay_hints(
+                                    InlayHintRefreshReason::RefreshRequested,
+                                    cx,
+                                );
+                                refresh_linked_ranges(editor, window, cx);
+                                editor.refresh_code_actions(window, cx);
+                                editor.refresh_document_highlights(cx);
+                            }
                         }
                     }
 
@@ -2029,7 +2041,7 @@ impl Editor {
             }));
         }
 
-        let buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 
         let inlay_hint_settings =
             inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
@@ -2066,8 +2078,8 @@ impl Editor {
                 update_uncommitted_diff_for_buffer(
                     cx.entity(),
                     &project,
-                    buffer.read(cx).all_buffers(),
-                    buffer.clone(),
+                    multi_buffer.read(cx).all_buffers(),
+                    multi_buffer.clone(),
                     cx,
                 )
                 .shared(),
@@ -2079,7 +2091,7 @@ impl Editor {
             focus_handle,
             show_cursor_when_unfocused: false,
             last_focused_descendant: None,
-            buffer: buffer.clone(),
+            buffer: multi_buffer.clone(),
             display_map: display_map.clone(),
             placeholder_display_map: None,
             selections,
@@ -2221,8 +2233,8 @@ impl Editor {
             _subscriptions: (!is_minimap)
                 .then(|| {
                     vec![
-                        cx.observe(&buffer, Self::on_buffer_changed),
-                        cx.subscribe_in(&buffer, window, Self::on_buffer_event),
+                        cx.observe(&multi_buffer, Self::on_buffer_changed),
+                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
                         cx.observe_in(&display_map, window, Self::on_display_map_changed),
                         cx.observe(&blink_manager, |_, _, cx| cx.notify()),
                         cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
@@ -2248,6 +2260,7 @@ impl Editor {
             colors: None,
             refresh_colors_task: Task::ready(()),
             next_color_inlay_id: 0,
+            post_scroll_update: Task::ready(()),
             linked_edit_ranges: Default::default(),
             in_project_search: false,
             previous_search_ranges: None,
@@ -2370,7 +2383,7 @@ impl Editor {
         editor.selection_history.mode = SelectionHistoryMode::Normal;
 
         editor.scroll_manager.show_scrollbars(window, cx);
-        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &buffer, cx);
+        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 
         if full_mode {
             let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
@@ -2382,24 +2395,13 @@ impl Editor {
 
             editor.go_to_active_debug_line(window, cx);
 
-            if let Some(buffer) = buffer.read(cx).as_singleton()
-                && let Some(project) = editor.project()
-            {
-                let handle = project.update(cx, |project, cx| {
-                    project.register_buffer_with_language_servers(&buffer, cx)
-                });
-                editor
-                    .registered_buffers
-                    .insert(buffer.read(cx).remote_id(), handle);
+            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
+                editor.register_buffer(buffer.read(cx).remote_id(), cx);
             }
 
             editor.minimap =
                 editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
             editor.colors = Some(LspColorData::new(cx));
-            editor.update_lsp_data(false, None, window, cx);
-        }
-
-        if editor.mode.is_full() {
             editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
         }
 
@@ -2899,20 +2901,6 @@ impl Editor {
         self.collapse_matches = collapse_matches;
     }
 
-    fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
-        let buffers = self.buffer.read(cx).all_buffers();
-        let Some(project) = self.project.as_ref() else {
-            return;
-        };
-        project.update(cx, |project, cx| {
-            for buffer in buffers {
-                self.registered_buffers
-                    .entry(buffer.read(cx).remote_id())
-                    .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
-            }
-        })
-    }
-
     pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
         if self.collapse_matches {
             return range.start..range.start;
@@ -3112,19 +3100,8 @@ impl Editor {
         }
 
         if local {
-            if let Some(buffer_id) = new_cursor_position.buffer_id
-                && !self.registered_buffers.contains_key(&buffer_id)
-                && let Some(project) = self.project.as_ref()
-            {
-                project.update(cx, |project, cx| {
-                    let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
-                        return;
-                    };
-                    self.registered_buffers.insert(
-                        buffer_id,
-                        project.register_buffer_with_language_servers(&buffer, cx),
-                    );
-                })
+            if let Some(buffer_id) = new_cursor_position.buffer_id {
+                self.register_buffer(buffer_id, cx);
             }
 
             let mut context_menu = self.context_menu.borrow_mut();
@@ -3174,11 +3151,12 @@ impl Editor {
             }
             self.refresh_code_actions(window, cx);
             self.refresh_document_highlights(cx);
+            refresh_linked_ranges(self, window, cx);
+
             self.refresh_selected_text_highlights(false, window, cx);
             refresh_matching_bracket_highlights(self, cx);
             self.update_visible_edit_prediction(window, cx);
             self.edit_prediction_requires_modifier_in_indent_conflict = true;
-            linked_editing_ranges::refresh_linked_ranges(self, window, cx);
             self.inline_blame_popover.take();
             if self.git_blame_inline_enabled {
                 self.start_inline_blame_timer(window, cx);
@@ -4444,7 +4422,7 @@ impl Editor {
                 }
             }
             this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
-            linked_editing_ranges::refresh_linked_ranges(this, window, cx);
+            refresh_linked_ranges(this, window, cx);
             this.refresh_edit_prediction(true, false, window, cx);
             jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
         });
@@ -5305,12 +5283,18 @@ impl Editor {
             }
         };
 
+        let mut visible_excerpts = self.visible_excerpts(required_languages.as_ref(), cx);
+        visible_excerpts.retain(|_, (buffer, _, _)| {
+            self.registered_buffers
+                .contains_key(&buffer.read(cx).remote_id())
+        });
+
         if let Some(InlaySplice {
             to_remove,
             to_insert,
         }) = self.inlay_hint_cache.spawn_hint_refresh(
             reason_description,
-            self.visible_excerpts(required_languages.as_ref(), cx),
+            visible_excerpts,
             invalidate_cache,
             ignore_debounce,
             cx,
@@ -10107,7 +10091,7 @@ impl Editor {
                 })
             }
             this.refresh_edit_prediction(true, false, window, cx);
-            linked_editing_ranges::refresh_linked_ranges(this, window, cx);
+            refresh_linked_ranges(this, window, cx);
         });
     }
 
@@ -16974,40 +16958,31 @@ impl Editor {
                 editor
             })
         });
-        editor.update(cx, |editor, cx| {
-            match multibuffer_selection_mode {
-                MultibufferSelectionMode::First => {
-                    if let Some(first_range) = ranges.first() {
-                        editor.change_selections(
-                            SelectionEffects::no_scroll(),
-                            window,
-                            cx,
-                            |selections| {
-                                selections.clear_disjoint();
-                                selections
-                                    .select_anchor_ranges(std::iter::once(first_range.clone()));
-                            },
-                        );
-                    }
-                    editor.highlight_background::<Self>(
-                        &ranges,
-                        |theme| theme.colors().editor_highlighted_line_background,
-                        cx,
-                    );
-                }
-                MultibufferSelectionMode::All => {
+        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
+            MultibufferSelectionMode::First => {
+                if let Some(first_range) = ranges.first() {
                     editor.change_selections(
                         SelectionEffects::no_scroll(),
                         window,
                         cx,
                         |selections| {
                             selections.clear_disjoint();
-                            selections.select_anchor_ranges(ranges);
+                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
                         },
                     );
                 }
+                editor.highlight_background::<Self>(
+                    &ranges,
+                    |theme| theme.colors().editor_highlighted_line_background,
+                    cx,
+                );
+            }
+            MultibufferSelectionMode::All => {
+                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
+                    selections.clear_disjoint();
+                    selections.select_anchor_ranges(ranges);
+                });
             }
-            editor.register_buffers_with_language_servers(cx);
         });
 
         let item = Box::new(editor);
@@ -17854,7 +17829,7 @@ impl Editor {
         window: &Window,
         cx: &mut Context<Self>,
     ) -> Option<()> {
-        if !self.mode().is_full() {
+        if self.ignore_lsp_data() {
             return None;
         }
         let pull_diagnostics_settings = ProjectSettings::get_global(cx)
@@ -17866,8 +17841,14 @@ impl Editor {
         let project = self.project()?.downgrade();
         let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
         let mut buffers = self.buffer.read(cx).all_buffers();
-        if let Some(buffer_id) = buffer_id {
-            buffers.retain(|buffer| buffer.read(cx).remote_id() == buffer_id);
+        buffers.retain(|buffer| {
+            let buffer_id_to_retain = buffer.read(cx).remote_id();
+            buffer_id.is_none_or(|buffer_id| buffer_id == buffer_id_to_retain)
+                && self.registered_buffers.contains_key(&buffer_id_to_retain)
+        });
+        if buffers.is_empty() {
+            self.pull_diagnostics_task = Task::ready(());
+            return None;
         }
 
         self.pull_diagnostics_task = cx.spawn_in(window, async move |editor, cx| {
@@ -20864,10 +20845,7 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         match event {
-            multi_buffer::Event::Edited {
-                singleton_buffer_edited,
-                edited_buffer,
-            } => {
+            multi_buffer::Event::Edited { edited_buffer } => {
                 self.scrollbar_marker_state.dirty = true;
                 self.active_indent_guides_state.dirty = true;
                 self.refresh_active_diagnostics(cx);
@@ -20878,31 +20856,16 @@ impl Editor {
                 if self.has_active_edit_prediction() {
                     self.update_visible_edit_prediction(window, cx);
                 }
-                if let Some(project) = self.project.as_ref()
-                    && let Some(edited_buffer) = edited_buffer
-                {
-                    project.update(cx, |project, cx| {
-                        self.registered_buffers
-                            .entry(edited_buffer.read(cx).remote_id())
-                            .or_insert_with(|| {
-                                project.register_buffer_with_language_servers(edited_buffer, cx)
-                            });
-                    });
-                }
-                cx.emit(EditorEvent::BufferEdited);
-                cx.emit(SearchEvent::MatchesInvalidated);
 
-                if let Some(buffer) = edited_buffer {
-                    self.update_lsp_data(false, Some(buffer.read(cx).remote_id()), window, cx);
-                }
-
-                if *singleton_buffer_edited {
-                    if let Some(buffer) = edited_buffer
-                        && buffer.read(cx).file().is_none()
-                    {
+                if let Some(edited_buffer) = edited_buffer {
+                    if edited_buffer.read(cx).file().is_none() {
                         cx.emit(EditorEvent::TitleChanged);
                     }
-                    if let Some(project) = &self.project {
+
+                    let buffer_id = edited_buffer.read(cx).remote_id();
+                    if let Some(project) = self.project.clone() {
+                        self.register_buffer(buffer_id, cx);
+                        self.update_lsp_data(Some(buffer_id), window, cx);
                         #[allow(clippy::mutable_key_type)]
                         let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
                             multibuffer
@@ -20929,6 +20892,9 @@ impl Editor {
                     }
                 }
 
+                cx.emit(EditorEvent::BufferEdited);
+                cx.emit(SearchEvent::MatchesInvalidated);
+
                 let Some(project) = &self.project else { return };
                 let (telemetry, is_via_ssh) = {
                     let project = project.read(cx);
@@ -20936,7 +20902,6 @@ impl Editor {
                     let is_via_ssh = project.is_via_remote_server();
                     (telemetry, is_via_ssh)
                 };
-                refresh_linked_ranges(self, window, cx);
                 telemetry.log_edit_event("editor", is_via_ssh);
             }
             multi_buffer::Event::ExcerptsAdded {
@@ -20958,24 +20923,22 @@ impl Editor {
                     )
                     .detach();
                 }
-                if self.active_diagnostics != ActiveDiagnostic::All {
-                    self.update_lsp_data(false, Some(buffer_id), window, cx);
-                }
+                self.update_lsp_data(Some(buffer_id), window, cx);
+                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
                 cx.emit(EditorEvent::ExcerptsAdded {
                     buffer: buffer.clone(),
                     predecessor: *predecessor,
                     excerpts: excerpts.clone(),
                 });
-                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
             }
             multi_buffer::Event::ExcerptsRemoved {
                 ids,
                 removed_buffer_ids,
             } => {
                 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
-                let buffer = self.buffer.read(cx);
-                self.registered_buffers
-                    .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
+                for buffer_id in removed_buffer_ids {
+                    self.registered_buffers.remove(buffer_id);
+                }
                 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
                 cx.emit(EditorEvent::ExcerptsRemoved {
                     ids: ids.clone(),
@@ -21007,7 +20970,7 @@ impl Editor {
                 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
             }
             multi_buffer::Event::LanguageChanged(buffer_id) => {
-                linked_editing_ranges::refresh_linked_ranges(self, window, cx);
+                self.registered_buffers.remove(&buffer_id);
                 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
                 cx.emit(EditorEvent::Reparsed(*buffer_id));
                 cx.notify();
@@ -21148,7 +21111,7 @@ impl Editor {
             if !inlay_splice.to_insert.is_empty() || !inlay_splice.to_remove.is_empty() {
                 self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
             }
-            self.refresh_colors(false, None, window, cx);
+            self.refresh_colors_for_visible_range(None, window, cx);
         }
 
         cx.notify();
@@ -22060,13 +22023,48 @@ impl Editor {
 
     fn update_lsp_data(
         &mut self,
-        ignore_cache: bool,
         for_buffer: Option<BufferId>,
         window: &mut Window,
         cx: &mut Context<'_, Self>,
     ) {
         self.pull_diagnostics(for_buffer, window, cx);
-        self.refresh_colors(ignore_cache, for_buffer, window, cx);
+        self.refresh_colors_for_visible_range(for_buffer, window, cx);
+    }
+
+    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
+        // Singletons are registered on editor creation.
+        if self.ignore_lsp_data() || self.buffer().read(cx).is_singleton() {
+            return;
+        }
+        for (_, (visible_buffer, _, _)) in self.visible_excerpts(None, cx) {
+            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
+        }
+    }
+
+    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) -> bool {
+        if !self.registered_buffers.contains_key(&buffer_id)
+            && let Some(project) = self.project.as_ref()
+        {
+            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
+                project.update(cx, |project, cx| {
+                    self.registered_buffers.insert(
+                        buffer_id,
+                        project.register_buffer_with_language_servers(&buffer, cx),
+                    );
+                });
+                return true;
+            } else {
+                self.registered_buffers.remove(&buffer_id);
+            }
+        }
+
+        false
+    }
+
+    fn ignore_lsp_data(&self) -> bool {
+        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
+        // skip any LSP updates for it.
+        self.active_diagnostics == ActiveDiagnostic::All || !self.mode().is_full()
     }
 }
 

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -2394,6 +2394,8 @@ pub mod tests {
                 editor.scroll_screen(&ScrollAmount::Page(1.0), window, cx);
             })
             .unwrap();
+        // Wait for the first hints request to fire off
+        cx.executor().advance_clock(Duration::from_millis(100));
         cx.executor().run_until_parked();
         editor
             .update(cx, |editor, window, cx| {
@@ -2752,6 +2754,23 @@ pub mod tests {
             })
             .unwrap();
         cx.executor().run_until_parked();
+        editor
+            .update(cx, |editor, _window, cx| {
+                let expected_hints = vec![
+                    "main hint #0".to_string(),
+                    "main hint #1".to_string(),
+                    "main hint #2".to_string(),
+                    "main hint #3".to_string(),
+                    "main hint #4".to_string(),
+                    "main hint #5".to_string(),
+                ];
+                assert_eq!(expected_hints, sorted_cached_hint_labels(editor),
+                    "New hints are not shown right after scrolling, we need to wait for the buffer to be registered");
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            })
+            .unwrap();
+        cx.executor().advance_clock(Duration::from_millis(100));
+        cx.executor().run_until_parked();
         editor
             .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
@@ -2766,7 +2785,7 @@ pub mod tests {
                     "other hint #2".to_string(),
                 ];
                 assert_eq!(expected_hints, sorted_cached_hint_labels(editor),
-                    "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
+                    "After scrolling to the new buffer and waiting for it to be registered, new hints should appear");
                 assert_eq!(expected_hints, visible_hint_labels(editor, cx));
             })
             .unwrap();
@@ -2853,15 +2872,18 @@ pub mod tests {
             })
             .unwrap();
         cx.executor().run_until_parked();
+        // Wait again to trigger the inlay hints fetch on scroll
+        cx.executor().advance_clock(Duration::from_millis(100));
+        cx.executor().run_until_parked();
         editor
             .update(cx, |editor, _window, cx| {
                 let expected_hints = vec![
-                    "main hint #0".to_string(),
-                    "main hint #1".to_string(),
-                    "main hint #2".to_string(),
-                    "main hint #3".to_string(),
-                    "main hint #4".to_string(),
-                    "main hint #5".to_string(),
+                    "main hint(edited) #0".to_string(),
+                    "main hint(edited) #1".to_string(),
+                    "main hint(edited) #2".to_string(),
+                    "main hint(edited) #3".to_string(),
+                    "main hint(edited) #4".to_string(),
+                    "main hint(edited) #5".to_string(),
                     "other hint(edited) #0".to_string(),
                     "other hint(edited) #1".to_string(),
                 ];

crates/editor/src/linked_editing_ranges.rs 🔗

@@ -48,7 +48,7 @@ pub(super) fn refresh_linked_ranges(
     window: &mut Window,
     cx: &mut Context<Editor>,
 ) -> Option<()> {
-    if editor.pending_rename.is_some() {
+    if editor.ignore_lsp_data() || editor.pending_rename.is_some() {
         return None;
     }
     let project = editor.project()?.downgrade();

crates/editor/src/lsp_colors.rs 🔗

@@ -2,11 +2,11 @@ use std::{cmp, ops::Range};
 
 use collections::HashMap;
 use futures::future::join_all;
-use gpui::{Hsla, Rgba};
+use gpui::{Hsla, Rgba, Task};
 use itertools::Itertools;
 use language::point_from_lsp;
 use multi_buffer::Anchor;
-use project::{DocumentColor, lsp_store::LspFetchStrategy};
+use project::DocumentColor;
 use settings::Settings as _;
 use text::{Bias, BufferId, OffsetRangeExt as _};
 use ui::{App, Context, Window};
@@ -143,14 +143,13 @@ impl LspColorData {
 }
 
 impl Editor {
-    pub(super) fn refresh_colors(
+    pub(super) fn refresh_colors_for_visible_range(
         &mut self,
-        ignore_cache: bool,
         buffer_id: Option<BufferId>,
         _: &Window,
         cx: &mut Context<Self>,
     ) {
-        if !self.mode().is_full() {
+        if self.ignore_lsp_data() {
             return;
         }
         let Some(project) = self.project.clone() else {
@@ -169,7 +168,9 @@ impl Editor {
             .into_values()
             .map(|(buffer, ..)| buffer)
             .filter(|editor_buffer| {
-                buffer_id.is_none_or(|buffer_id| buffer_id == editor_buffer.read(cx).remote_id())
+                let editor_buffer_id = editor_buffer.read(cx).remote_id();
+                buffer_id.is_none_or(|buffer_id| buffer_id == editor_buffer_id)
+                    && self.registered_buffers.contains_key(&editor_buffer_id)
             })
             .unique_by(|buffer| buffer.read(cx).remote_id())
             .collect::<Vec<_>>();
@@ -179,21 +180,20 @@ impl Editor {
                 .into_iter()
                 .filter_map(|buffer| {
                     let buffer_id = buffer.read(cx).remote_id();
-                    let fetch_strategy = if ignore_cache {
-                        LspFetchStrategy::IgnoreCache
-                    } else {
-                        LspFetchStrategy::UseCache {
-                            known_cache_version: self.colors.as_ref().and_then(|colors| {
-                                Some(colors.buffer_colors.get(&buffer_id)?.cache_version_used)
-                            }),
-                        }
-                    };
-                    let colors_task = lsp_store.document_colors(fetch_strategy, buffer, cx)?;
+                    let known_cache_version = self.colors.as_ref().and_then(|colors| {
+                        Some(colors.buffer_colors.get(&buffer_id)?.cache_version_used)
+                    });
+                    let colors_task = lsp_store.document_colors(known_cache_version, buffer, cx)?;
                     Some(async move { (buffer_id, colors_task.await) })
                 })
                 .collect::<Vec<_>>()
         });
 
+        if all_colors_task.is_empty() {
+            self.refresh_colors_task = Task::ready(());
+            return;
+        }
+
         self.refresh_colors_task = cx.spawn(async move |editor, cx| {
             cx.background_executor()
                 .timer(FETCH_COLORS_DEBOUNCE_TIMEOUT)

crates/editor/src/scroll.rs 🔗

@@ -494,15 +494,15 @@ impl Editor {
         let opened_first_time = self.scroll_manager.visible_line_count.is_none();
         self.scroll_manager.visible_line_count = Some(lines);
         if opened_first_time {
-            cx.spawn_in(window, async move |editor, cx| {
+            self.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
                 editor
                     .update_in(cx, |editor, window, cx| {
+                        editor.register_visible_buffers(cx);
                         editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
-                        editor.refresh_colors(false, None, window, cx);
+                        editor.update_lsp_data(None, window, cx);
                     })
-                    .ok()
-            })
-            .detach()
+                    .ok();
+            });
         }
     }
 
@@ -613,8 +613,19 @@ impl Editor {
             cx,
         );
 
-        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
-        self.refresh_colors(false, None, window, cx);
+        self.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
+            cx.background_executor()
+                .timer(Duration::from_millis(50))
+                .await;
+            editor
+                .update_in(cx, |editor, window, cx| {
+                    editor.register_visible_buffers(cx);
+                    editor.refresh_colors_for_visible_range(None, window, cx);
+                    editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
+                })
+                .ok();
+        });
+
         editor_was_scrolled
     }
 

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -99,7 +99,6 @@ pub enum Event {
     },
     DiffHunksToggled,
     Edited {
-        singleton_buffer_edited: bool,
         edited_buffer: Option<Entity<Buffer>>,
     },
     TransactionUndone {
@@ -2033,7 +2032,6 @@ impl MultiBuffer {
             DiffChangeKind::BufferEdited,
         );
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
         cx.emit(Event::ExcerptsAdded {
@@ -2074,7 +2072,6 @@ impl MultiBuffer {
             DiffChangeKind::BufferEdited,
         );
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
         cx.emit(Event::ExcerptsRemoved {
@@ -2361,7 +2358,6 @@ impl MultiBuffer {
         self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
         self.buffer_changed_since_sync.replace(true);
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
         cx.emit(Event::ExcerptsRemoved {
@@ -2429,7 +2425,6 @@ impl MultiBuffer {
         use language::BufferEvent;
         cx.emit(match event {
             BufferEvent::Edited => Event::Edited {
-                singleton_buffer_edited: true,
                 edited_buffer: Some(buffer),
             },
             BufferEvent::DirtyChanged => Event::DirtyChanged,
@@ -2528,7 +2523,6 @@ impl MultiBuffer {
             },
         );
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
     }
@@ -2808,7 +2802,6 @@ impl MultiBuffer {
         );
         cx.emit(Event::DiffHunksToggled);
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
     }
@@ -2892,7 +2885,6 @@ impl MultiBuffer {
 
         self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
         cx.emit(Event::ExcerptsExpanded { ids: vec![id] });
@@ -2997,7 +2989,6 @@ impl MultiBuffer {
 
         self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
         cx.emit(Event::Edited {
-            singleton_buffer_edited: false,
             edited_buffer: None,
         });
         cx.emit(Event::ExcerptsExpanded { ids });

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -157,15 +157,12 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
         events.read().as_slice(),
         &[
             Event::Edited {
-                singleton_buffer_edited: false,
                 edited_buffer: None,
             },
             Event::Edited {
-                singleton_buffer_edited: false,
                 edited_buffer: None,
             },
             Event::Edited {
-                singleton_buffer_edited: false,
                 edited_buffer: None,
             }
         ]

crates/project/src/lsp_store.rs 🔗

@@ -3495,12 +3495,6 @@ struct CodeLensData {
     update: Option<(Global, CodeLensTask)>,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum LspFetchStrategy {
-    IgnoreCache,
-    UseCache { known_cache_version: Option<usize> },
-}
-
 #[derive(Debug)]
 pub enum LspStoreEvent {
     LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
@@ -6498,47 +6492,35 @@ impl LspStore {
 
     pub fn document_colors(
         &mut self,
-        fetch_strategy: LspFetchStrategy,
+        known_cache_version: Option<usize>,
         buffer: Entity<Buffer>,
         cx: &mut Context<Self>,
     ) -> Option<DocumentColorTask> {
         let version_queried_for = buffer.read(cx).version();
         let buffer_id = buffer.read(cx).remote_id();
 
-        match fetch_strategy {
-            LspFetchStrategy::IgnoreCache => {}
-            LspFetchStrategy::UseCache {
-                known_cache_version,
-            } => {
-                if let Some(cached_data) = self.lsp_document_colors.get(&buffer_id)
-                    && !version_queried_for.changed_since(&cached_data.colors_for_version)
-                {
-                    let has_different_servers = self.as_local().is_some_and(|local| {
-                        local
-                            .buffers_opened_in_servers
-                            .get(&buffer_id)
-                            .cloned()
-                            .unwrap_or_default()
-                            != cached_data.colors.keys().copied().collect()
-                    });
-                    if !has_different_servers {
-                        if Some(cached_data.cache_version) == known_cache_version {
-                            return None;
-                        } else {
-                            return Some(
-                                Task::ready(Ok(DocumentColors {
-                                    colors: cached_data
-                                        .colors
-                                        .values()
-                                        .flatten()
-                                        .cloned()
-                                        .collect(),
-                                    cache_version: Some(cached_data.cache_version),
-                                }))
-                                .shared(),
-                            );
-                        }
-                    }
+        if let Some(cached_data) = self.lsp_document_colors.get(&buffer_id)
+            && !version_queried_for.changed_since(&cached_data.colors_for_version)
+        {
+            let has_different_servers = self.as_local().is_some_and(|local| {
+                local
+                    .buffers_opened_in_servers
+                    .get(&buffer_id)
+                    .cloned()
+                    .unwrap_or_default()
+                    != cached_data.colors.keys().copied().collect()
+            });
+            if !has_different_servers {
+                if Some(cached_data.cache_version) == known_cache_version {
+                    return None;
+                } else {
+                    return Some(
+                        Task::ready(Ok(DocumentColors {
+                            colors: cached_data.colors.values().flatten().cloned().collect(),
+                            cache_version: Some(cached_data.cache_version),
+                        }))
+                        .shared(),
+                    );
                 }
             }
         }
@@ -6564,13 +6546,12 @@ impl LspStore {
                     .map_err(Arc::new);
                 let fetched_colors = match fetched_colors {
                     Ok(fetched_colors) => {
-                        if fetch_strategy != LspFetchStrategy::IgnoreCache
-                            && Some(true)
-                                == buffer
-                                    .update(cx, |buffer, _| {
-                                        buffer.version() != query_version_queried_for
-                                    })
-                                    .ok()
+                        if Some(true)
+                            == buffer
+                                .update(cx, |buffer, _| {
+                                    buffer.version() != query_version_queried_for
+                                })
+                                .ok()
                         {
                             return Ok(DocumentColors::default());
                         }

crates/project/src/manifest_tree/server_tree.rs 🔗

@@ -47,7 +47,7 @@ pub struct LanguageServerTree {
 /// A node in language server tree represents either:
 /// - A language server that has already been initialized/updated for a given project
 /// - A soon-to-be-initialized language server.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct LanguageServerTreeNode(Weak<InnerTreeNode>);
 
 /// Describes a request to launch a language server.