Detailed changes
@@ -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();
@@ -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()
}
}
@@ -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(),
];
@@ -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();
@@ -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)
@@ -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
}
@@ -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 });
@@ -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,
}
]
@@ -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());
}
@@ -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.