From 0c11d841e8641074e30af8f1f8f0928df109942c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 7 May 2024 15:31:07 +0200 Subject: [PATCH] editor: Move runnables querying to background thread (#11487) Originally reported by @mrnugget and @bennetbo Also, instead of requerying them every frame, we do so whenever buffer changes. As a bonus, I modified tree-sitter query for Rust tests. Release Notes: - N/A --- crates/editor/src/editor.rs | 90 +++++++++++++++++++------ crates/editor/src/element.rs | 37 ++++------ crates/languages/src/rust/runnables.scm | 2 +- 3 files changed, 83 insertions(+), 46 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b9756364ec..b5c6e3fbc7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -505,6 +505,7 @@ pub struct Editor { last_bounds: Option>, expect_bounds_change: Option>, tasks: HashMap, + tasks_update_task: Option>, } #[derive(Clone)] @@ -1688,8 +1689,9 @@ impl Editor { }); }), ], + tasks_update_task: None, }; - + this.tasks_update_task = Some(this.refresh_runnables(cx)); this._subscriptions.extend(project_subscriptions); this.end_selection(cx); @@ -7687,25 +7689,68 @@ impl Editor { self.select_larger_syntax_node_stack = stack; } - fn runnable_display_rows( - &self, - range: Range, + fn refresh_runnables(&mut self, cx: &mut ViewContext) -> Task<()> { + let project = self.project.clone(); + cx.spawn(|this, mut cx| async move { + let Ok(display_snapshot) = this.update(&mut cx, |this, cx| { + this.display_map.update(cx, |map, cx| map.snapshot(cx)) + }) else { + return; + }; + + let Some(project) = project else { + return; + }; + if project + .update(&mut cx, |this, _| this.is_remote()) + .unwrap_or(true) + { + // Do not display any test indicators in remote projects. + return; + } + let new_rows = + cx.background_executor() + .spawn({ + let snapshot = display_snapshot.clone(); + async move { + Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max()) + } + }) + .await; + let rows = Self::refresh_runnable_display_rows( + project, + display_snapshot, + new_rows, + cx.clone(), + ); + + this.update(&mut cx, |this, _| { + this.clear_tasks(); + for (row, tasks) in rows { + this.insert_tasks(row, tasks); + } + }) + .ok(); + }) + } + fn fetch_runnable_ranges( snapshot: &DisplaySnapshot, - cx: &WindowContext, + range: Range, + ) -> Vec<(Range, Runnable)> { + snapshot.buffer_snapshot.runnable_ranges(range).collect() + } + fn refresh_runnable_display_rows( + project: Model, + snapshot: DisplaySnapshot, + runnable_ranges: Vec<(Range, Runnable)>, + mut cx: AsyncWindowContext, ) -> Vec<(u32, RunnableTasks)> { - if self - .project - .as_ref() - .map_or(false, |project| project.read(cx).is_remote()) - { - // Do not display any test indicators in remote projects. - return vec![]; - } - snapshot - .buffer_snapshot - .runnable_ranges(range) + runnable_ranges + .into_iter() .filter_map(|(multi_buffer_range, mut runnable)| { - let (tasks, _) = self.resolve_runnable(&mut runnable, cx); + let (tasks, _) = cx + .update(|cx| Self::resolve_runnable(project.clone(), &mut runnable, cx)) + .ok()?; if tasks.is_empty() { return None; } @@ -7722,13 +7767,10 @@ impl Editor { } fn resolve_runnable( - &self, + project: Model, runnable: &mut Runnable, cx: &WindowContext<'_>, ) -> (Vec<(TaskSourceKind, TaskTemplate)>, Option) { - let Some(project) = self.project.as_ref() else { - return Default::default(); - }; let (inventory, worktree_id) = project.read_with(cx, |project, cx| { let worktree_id = project .buffer_for_id(runnable.buffer) @@ -10070,7 +10112,11 @@ impl Editor { self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx); cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() }) } - multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed), + multi_buffer::Event::Reparsed => { + self.tasks_update_task = Some(self.refresh_runnables(cx)); + + cx.emit(EditorEvent::Reparsed); + } multi_buffer::Event::LanguageChanged => { cx.emit(EditorEvent::Reparsed); cx.notify(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 20850ffb2d..8f7eeea3e8 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -15,8 +15,8 @@ use crate::{ CodeActionsMenu, CursorShape, DisplayPoint, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, GutterDimensions, HalfPageDown, HalfPageUp, HoveredCursor, HunkToExpand, LineDown, LineUp, - OpenExcerpts, PageDown, PageUp, Point, RunnableTasks, SelectPhase, Selection, SoftWrap, - ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN, + OpenExcerpts, PageDown, PageUp, Point, SelectPhase, Selection, SoftWrap, ToPoint, + CURSORS_VISIBLE_FOR, MAX_LINE_LEN, }; use anyhow::Result; use client::ParticipantIndex; @@ -1377,7 +1377,6 @@ impl EditorElement { fn layout_run_indicators( &self, - task_lines: Vec<(u32, RunnableTasks)>, line_height: Pixels, scroll_pixel_position: gpui::Point, gutter_dimensions: &GutterDimensions, @@ -1385,8 +1384,6 @@ impl EditorElement { cx: &mut WindowContext, ) -> Vec { self.editor.update(cx, |editor, cx| { - editor.clear_tasks(); - let active_task_indicator_row = if let Some(crate::ContextMenu::CodeActions(CodeActionsMenu { deployed_from_indicator, @@ -1402,21 +1399,20 @@ impl EditorElement { } else { None }; - task_lines - .into_iter() - .map(|(row, tasks)| { - editor.insert_tasks(row, tasks); - + editor + .tasks + .keys() + .map(|row| { let button = editor.render_run_indicator( &self.style, - Some(row) == active_task_indicator_row, - row, + Some(*row) == active_task_indicator_row, + *row, cx, ); let button = prepaint_gutter_button( button, - row, + *row, line_height, gutter_dimensions, scroll_pixel_position, @@ -3835,12 +3831,6 @@ impl Element for EditorElement { cx, ); - let test_lines = self.editor.read(cx).runnable_display_rows( - start_anchor..end_anchor, - &snapshot.display_snapshot, - cx, - ); - let (selections, active_rows, newest_selection_head) = self.layout_selections( start_anchor, end_anchor, @@ -4030,9 +4020,11 @@ impl Element for EditorElement { cx, ); if gutter_settings.code_actions { - let has_test_indicator = test_lines - .iter() - .any(|(line, _)| *line == newest_selection_head.row()); + let has_test_indicator = self + .editor + .read(cx) + .tasks + .contains_key(&newest_selection_head.row()); if !has_test_indicator { code_actions_indicator = self.layout_code_actions_indicator( line_height, @@ -4048,7 +4040,6 @@ impl Element for EditorElement { } let test_indicators = self.layout_run_indicators( - test_lines, line_height, scroll_pixel_position, &gutter_dimensions, diff --git a/crates/languages/src/rust/runnables.scm b/crates/languages/src/rust/runnables.scm index 47035bb0b3..16f43f60e3 100644 --- a/crates/languages/src/rust/runnables.scm +++ b/crates/languages/src/rust/runnables.scm @@ -1,6 +1,6 @@ ( (attribute_item (attribute) @_attribute - (#match? @_attribute ".*test.*")) + (#match? @_attribute ".*test")) . (function_item name: (_) @run)