crates/editor/src/actions.rs 🔗
@@ -87,6 +87,7 @@ pub struct ToggleCodeActions {
#[derive(PartialEq, Clone, Debug)]
pub enum CodeActionSource {
Indicator(DisplayRow),
+ RunMenu(DisplayRow),
QuickActionBar,
}
Conrad Irwin and Anthony Eid created
Now if you click the triangle you get runnables, if you click the
lightning bolt you get code actions, if you trigger the code actions
menu with the mouse/keyboard you still get both.
Release Notes:
- Fixed the run/code actions menu to not duplicate content when opened
from the respective icons.
---------
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
crates/editor/src/actions.rs | 1
crates/editor/src/code_context_menus.rs | 4
crates/editor/src/editor.rs | 320 ++++++++++++++------------
3 files changed, 175 insertions(+), 150 deletions(-)
@@ -87,6 +87,7 @@ pub struct ToggleCodeActions {
#[derive(PartialEq, Clone, Debug)]
pub enum CodeActionSource {
Indicator(DisplayRow),
+ RunMenu(DisplayRow),
QuickActionBar,
}
@@ -1401,7 +1401,9 @@ impl CodeActionsMenu {
fn origin(&self) -> ContextMenuOrigin {
match &self.deployed_from {
- Some(CodeActionSource::Indicator(row)) => ContextMenuOrigin::GutterIndicator(*row),
+ Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
+ ContextMenuOrigin::GutterIndicator(*row)
+ }
Some(CodeActionSource::QuickActionBar) => ContextMenuOrigin::QuickActionBar,
None => ContextMenuOrigin::Cursor,
}
@@ -5711,70 +5711,60 @@ impl Editor {
drop(context_menu);
let snapshot = self.snapshot(window, cx);
let deployed_from = action.deployed_from.clone();
- let mut task = self.code_actions_task.take();
let action = action.clone();
- cx.spawn_in(window, async move |editor, cx| {
- while let Some(prev_task) = task {
- prev_task.await.log_err();
- task = editor.update(cx, |this, _| this.code_actions_task.take())?;
+ self.completion_tasks.clear();
+ self.discard_inline_completion(false, cx);
+
+ let multibuffer_point = match &action.deployed_from {
+ Some(CodeActionSource::Indicator(row)) => {
+ DisplayPoint::new(*row, 0).to_point(&snapshot)
}
+ _ => self.selections.newest::<Point>(cx).head(),
+ };
+ let Some((buffer, buffer_row)) = snapshot
+ .buffer_snapshot
+ .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
+ .and_then(|(buffer_snapshot, range)| {
+ self.buffer()
+ .read(cx)
+ .buffer(buffer_snapshot.remote_id())
+ .map(|buffer| (buffer, range.start.row))
+ })
+ else {
+ return;
+ };
+ let buffer_id = buffer.read(cx).remote_id();
+ let tasks = self
+ .tasks
+ .get(&(buffer_id, buffer_row))
+ .map(|t| Arc::new(t.to_owned()));
- let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
- if editor.focus_handle.is_focused(window) {
- let multibuffer_point = match &action.deployed_from {
- Some(CodeActionSource::Indicator(row)) => {
- DisplayPoint::new(*row, 0).to_point(&snapshot)
- }
- _ => editor.selections.newest::<Point>(cx).head(),
- };
- let (buffer, buffer_row) = snapshot
- .buffer_snapshot
- .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
- .and_then(|(buffer_snapshot, range)| {
- editor
- .buffer
- .read(cx)
- .buffer(buffer_snapshot.remote_id())
- .map(|buffer| (buffer, range.start.row))
- })?;
- let (_, code_actions) = editor
- .available_code_actions
- .clone()
- .and_then(|(location, code_actions)| {
- let snapshot = location.buffer.read(cx).snapshot();
- let point_range = location.range.to_point(&snapshot);
- let point_range = point_range.start.row..=point_range.end.row;
- if point_range.contains(&buffer_row) {
- Some((location, code_actions))
- } else {
- None
- }
- })
- .unzip();
- let buffer_id = buffer.read(cx).remote_id();
- let tasks = editor
- .tasks
- .get(&(buffer_id, buffer_row))
- .map(|t| Arc::new(t.to_owned()));
- if tasks.is_none() && code_actions.is_none() {
- return None;
+ if !self.focus_handle.is_focused(window) {
+ return;
+ }
+ let project = self.project.clone();
+
+ let code_actions_task = match deployed_from {
+ Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
+ _ => self.code_actions(buffer_row, window, cx),
+ };
+
+ let runnable_task = match deployed_from {
+ Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
+ _ => {
+ let mut task_context_task = Task::ready(None);
+ if let Some(tasks) = &tasks {
+ if let Some(project) = project {
+ task_context_task =
+ Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
}
+ }
- editor.completion_tasks.clear();
- editor.discard_inline_completion(false, cx);
- let task_context =
- tasks
- .as_ref()
- .zip(editor.project.clone())
- .map(|(tasks, project)| {
- Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
- });
+ cx.spawn_in(window, {
+ let buffer = buffer.clone();
+ async move |editor, cx| {
+ let task_context = task_context_task.await;
- Some(cx.spawn_in(window, async move |editor, cx| {
- let task_context = match task_context {
- Some(task_context) => task_context.await,
- None => None,
- };
let resolved_tasks =
tasks
.zip(task_context.clone())
@@ -5786,103 +5776,135 @@ impl Editor {
)),
});
let debug_scenarios = editor.update(cx, |editor, cx| {
- if cx.has_flag::<DebuggerFeatureFlag>() {
- maybe!({
- let project = editor.project.as_ref()?;
- let dap_store = project.read(cx).dap_store();
- let mut scenarios = vec![];
- let resolved_tasks = resolved_tasks.as_ref()?;
- let buffer = buffer.read(cx);
- let language = buffer.language()?;
- let file = buffer.file();
- let debug_adapter =
- language_settings(language.name().into(), file, cx)
- .debuggers
- .first()
- .map(SharedString::from)
- .or_else(|| {
- language
- .config()
- .debuggers
- .first()
- .map(SharedString::from)
- })?;
-
- dap_store.update(cx, |dap_store, cx| {
- for (_, task) in &resolved_tasks.templates {
- if let Some(scenario) = dap_store
- .debug_scenario_for_build_task(
- task.original_task().clone(),
- debug_adapter.clone().into(),
- task.display_label().to_owned().into(),
- cx,
- )
- {
- scenarios.push(scenario);
- }
- }
- });
- Some(scenarios)
- })
- .unwrap_or_default()
- } else {
- vec![]
- }
+ editor.debug_scenarios(&resolved_tasks, &buffer, cx)
})?;
- let spawn_straight_away = quick_launch
- && resolved_tasks
- .as_ref()
- .map_or(false, |tasks| tasks.templates.len() == 1)
- && code_actions
- .as_ref()
- .map_or(true, |actions| actions.is_empty())
- && debug_scenarios.is_empty();
- if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
- crate::hover_popover::hide_hover(editor, cx);
- *editor.context_menu.borrow_mut() =
- Some(CodeContextMenu::CodeActions(CodeActionsMenu {
- buffer,
- actions: CodeActionContents::new(
- resolved_tasks,
- code_actions,
- debug_scenarios,
- task_context.unwrap_or_default(),
- ),
- selected_item: Default::default(),
- scroll_handle: UniformListScrollHandle::default(),
- deployed_from,
- }));
- if spawn_straight_away {
- if let Some(task) = editor.confirm_code_action(
- &ConfirmCodeAction { item_ix: Some(0) },
- window,
- cx,
- ) {
- cx.notify();
- return task;
- }
- }
- cx.notify();
- Task::ready(Ok(()))
- }) {
- task.await
- } else {
- Ok(())
- }
- }))
- } else {
- Some(Task::ready(Ok(())))
- }
- })?;
- if let Some(task) = spawned_test_task {
- task.await?;
+ anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
+ }
+ })
}
+ };
- anyhow::Ok(())
+ cx.spawn_in(window, async move |editor, cx| {
+ let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
+ let code_actions = code_actions_task.await;
+ let spawn_straight_away = quick_launch
+ && resolved_tasks
+ .as_ref()
+ .map_or(false, |tasks| tasks.templates.len() == 1)
+ && code_actions
+ .as_ref()
+ .map_or(true, |actions| actions.is_empty())
+ && debug_scenarios.is_empty();
+
+ editor.update_in(cx, |editor, window, cx| {
+ crate::hover_popover::hide_hover(editor, cx);
+ *editor.context_menu.borrow_mut() =
+ Some(CodeContextMenu::CodeActions(CodeActionsMenu {
+ buffer,
+ actions: CodeActionContents::new(
+ resolved_tasks,
+ code_actions,
+ debug_scenarios,
+ task_context.unwrap_or_default(),
+ ),
+ selected_item: Default::default(),
+ scroll_handle: UniformListScrollHandle::default(),
+ deployed_from,
+ }));
+ if spawn_straight_away {
+ if let Some(task) = editor.confirm_code_action(
+ &ConfirmCodeAction { item_ix: Some(0) },
+ window,
+ cx,
+ ) {
+ cx.notify();
+ return task;
+ }
+ }
+
+ Task::ready(Ok(()))
+ })
})
.detach_and_log_err(cx);
}
+ fn debug_scenarios(
+ &mut self,
+ resolved_tasks: &Option<ResolvedTasks>,
+ buffer: &Entity<Buffer>,
+ cx: &mut App,
+ ) -> Vec<task::DebugScenario> {
+ if cx.has_flag::<DebuggerFeatureFlag>() {
+ maybe!({
+ let project = self.project.as_ref()?;
+ let dap_store = project.read(cx).dap_store();
+ let mut scenarios = vec![];
+ let resolved_tasks = resolved_tasks.as_ref()?;
+ let buffer = buffer.read(cx);
+ let language = buffer.language()?;
+ let file = buffer.file();
+ let debug_adapter = language_settings(language.name().into(), file, cx)
+ .debuggers
+ .first()
+ .map(SharedString::from)
+ .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
+
+ dap_store.update(cx, |dap_store, cx| {
+ for (_, task) in &resolved_tasks.templates {
+ if let Some(scenario) = dap_store.debug_scenario_for_build_task(
+ task.original_task().clone(),
+ debug_adapter.clone().into(),
+ task.display_label().to_owned().into(),
+ cx,
+ ) {
+ scenarios.push(scenario);
+ }
+ }
+ });
+ Some(scenarios)
+ })
+ .unwrap_or_default()
+ } else {
+ vec![]
+ }
+ }
+
+ fn code_actions(
+ &mut self,
+ buffer_row: u32,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
+ let mut task = self.code_actions_task.take();
+ cx.spawn_in(window, async move |editor, cx| {
+ while let Some(prev_task) = task {
+ prev_task.await.log_err();
+ task = editor
+ .update(cx, |this, _| this.code_actions_task.take())
+ .ok()?;
+ }
+
+ editor
+ .update(cx, |editor, cx| {
+ editor
+ .available_code_actions
+ .clone()
+ .and_then(|(location, code_actions)| {
+ let snapshot = location.buffer.read(cx).snapshot();
+ let point_range = location.range.to_point(&snapshot);
+ let point_range = point_range.start.row..=point_range.end.row;
+ if point_range.contains(&buffer_row) {
+ Some(code_actions)
+ } else {
+ None
+ }
+ })
+ })
+ .ok()
+ .flatten()
+ })
+ }
+
pub fn confirm_code_action(
&mut self,
action: &ConfirmCodeAction,
@@ -7920,7 +7942,7 @@ impl Editor {
window.focus(&editor.focus_handle(cx));
editor.toggle_code_actions(
&ToggleCodeActions {
- deployed_from: Some(CodeActionSource::Indicator(row)),
+ deployed_from: Some(CodeActionSource::RunMenu(row)),
quick_launch,
},
window,