Fix code actions run confusion (#32579)
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>
This commit is contained in:
parent
9032ea9849
commit
2a63c5f951
3 changed files with 175 additions and 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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue