debugger/tasks: Remove TaskType enum (#29208)

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Piotr Osiewicz 2025-04-26 01:44:56 +02:00 committed by GitHub
parent 053fafa90e
commit 67615b968b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1272 additions and 1114 deletions

View file

@ -78,6 +78,10 @@ pub struct ToggleCodeActions {
#[serde(default)]
#[serde(skip)]
pub deployed_from_indicator: Option<DisplayRow>,
// Run first available task if there is only one.
#[serde(default)]
#[serde(skip)]
pub quick_launch: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]

View file

@ -1,4 +1,3 @@
use feature_flags::{DebuggerFeatureFlag, FeatureFlagAppExt as _};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
AnyElement, BackgroundExecutor, Entity, Focusable, FontWeight, ListSizingBehavior,
@ -13,6 +12,8 @@ use ordered_float::OrderedFloat;
use project::CompletionSource;
use project::lsp_store::CompletionDocumentation;
use project::{CodeAction, Completion, TaskSourceKind};
use task::DebugScenario;
use task::TaskContext;
use std::{
cell::RefCell,
@ -39,6 +40,7 @@ pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
pub const MENU_ASIDE_MIN_WIDTH: Pixels = px(260.);
pub const MENU_ASIDE_MAX_WIDTH: Pixels = px(500.);
#[allow(clippy::large_enum_variant)]
pub enum CodeContextMenu {
Completions(CompletionsMenu),
CodeActions(CodeActionsMenu),
@ -819,28 +821,25 @@ pub struct AvailableCodeAction {
}
#[derive(Clone)]
pub struct CodeActionContents {
pub(crate) struct CodeActionContents {
tasks: Option<Rc<ResolvedTasks>>,
actions: Option<Rc<[AvailableCodeAction]>>,
debug_scenarios: Vec<DebugScenario>,
pub(crate) context: TaskContext,
}
impl CodeActionContents {
pub fn new(
mut tasks: Option<ResolvedTasks>,
pub(crate) fn new(
tasks: Option<ResolvedTasks>,
actions: Option<Rc<[AvailableCodeAction]>>,
cx: &App,
debug_scenarios: Vec<DebugScenario>,
context: TaskContext,
) -> Self {
if !cx.has_flag::<DebuggerFeatureFlag>() {
if let Some(tasks) = &mut tasks {
tasks
.templates
.retain(|(_, task)| !matches!(task.task_type(), task::TaskType::Debug(_)));
}
}
Self {
tasks: tasks.map(Rc::new),
actions,
debug_scenarios,
context,
}
}
@ -849,21 +848,13 @@ impl CodeActionContents {
}
fn len(&self) -> usize {
match (&self.tasks, &self.actions) {
(Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
(Some(tasks), None) => tasks.templates.len(),
(None, Some(actions)) => actions.len(),
(None, None) => 0,
}
let tasks_len = self.tasks.as_ref().map_or(0, |tasks| tasks.templates.len());
let code_actions_len = self.actions.as_ref().map_or(0, |actions| actions.len());
tasks_len + code_actions_len + self.debug_scenarios.len()
}
fn is_empty(&self) -> bool {
match (&self.tasks, &self.actions) {
(Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
(Some(tasks), None) => tasks.templates.is_empty(),
(None, Some(actions)) => actions.is_empty(),
(None, None) => true,
}
self.len() == 0
}
fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
@ -882,43 +873,38 @@ impl CodeActionContents {
provider: available.provider.clone(),
})
}))
.chain(
self.debug_scenarios
.iter()
.cloned()
.map(CodeActionsItem::DebugScenario),
)
}
pub fn get(&self, index: usize) -> Option<CodeActionsItem> {
match (&self.tasks, &self.actions) {
(Some(tasks), Some(actions)) => {
if index < tasks.templates.len() {
tasks
.templates
.get(index)
.cloned()
.map(|(kind, task)| CodeActionsItem::Task(kind, task))
} else {
actions.get(index - tasks.templates.len()).map(|available| {
CodeActionsItem::CodeAction {
excerpt_id: available.excerpt_id,
action: available.action.clone(),
provider: available.provider.clone(),
}
})
}
pub fn get(&self, mut index: usize) -> Option<CodeActionsItem> {
if let Some(tasks) = &self.tasks {
if let Some((kind, task)) = tasks.templates.get(index) {
return Some(CodeActionsItem::Task(kind.clone(), task.clone()));
} else {
index -= tasks.templates.len();
}
(Some(tasks), None) => tasks
.templates
.get(index)
.cloned()
.map(|(kind, task)| CodeActionsItem::Task(kind, task)),
(None, Some(actions)) => {
actions
.get(index)
.map(|available| CodeActionsItem::CodeAction {
excerpt_id: available.excerpt_id,
action: available.action.clone(),
provider: available.provider.clone(),
})
}
(None, None) => None,
}
if let Some(actions) = &self.actions {
if let Some(available) = actions.get(index) {
return Some(CodeActionsItem::CodeAction {
excerpt_id: available.excerpt_id,
action: available.action.clone(),
provider: available.provider.clone(),
});
} else {
index -= actions.len();
}
}
self.debug_scenarios
.get(index)
.cloned()
.map(CodeActionsItem::DebugScenario)
}
}
@ -931,6 +917,7 @@ pub enum CodeActionsItem {
action: CodeAction,
provider: Rc<dyn CodeActionProvider>,
},
DebugScenario(DebugScenario),
}
impl CodeActionsItem {
@ -947,16 +934,23 @@ impl CodeActionsItem {
};
Some(action)
}
fn as_debug_scenario(&self) -> Option<&DebugScenario> {
let Self::DebugScenario(scenario) = self else {
return None;
};
Some(scenario)
}
pub fn label(&self) -> String {
match self {
Self::CodeAction { action, .. } => action.lsp_action.title().to_owned(),
Self::Task(_, task) => task.resolved_label.clone(),
Self::DebugScenario(scenario) => scenario.label.to_string(),
}
}
}
pub struct CodeActionsMenu {
pub(crate) struct CodeActionsMenu {
pub actions: CodeActionContents,
pub buffer: Entity<Buffer>,
pub selected_item: usize,
@ -1065,19 +1059,7 @@ impl CodeActionsMenu {
.inset(true)
.toggle_state(selected)
.when_some(action.as_code_action(), |this, action| {
this.on_click(cx.listener(move |editor, _, window, cx| {
cx.stop_propagation();
if let Some(task) = editor.confirm_code_action(
&ConfirmCodeAction {
item_ix: Some(item_ix),
},
window,
cx,
) {
task.detach_and_log_err(cx)
}
}))
.child(
this.child(
h_flex()
.overflow_hidden()
.child(
@ -1090,19 +1072,7 @@ impl CodeActionsMenu {
)
})
.when_some(action.as_task(), |this, task| {
this.on_click(cx.listener(move |editor, _, window, cx| {
cx.stop_propagation();
if let Some(task) = editor.confirm_code_action(
&ConfirmCodeAction {
item_ix: Some(item_ix),
},
window,
cx,
) {
task.detach_and_log_err(cx)
}
}))
.child(
this.child(
h_flex()
.overflow_hidden()
.child(task.resolved_label.replace("\n", ""))
@ -1110,7 +1080,29 @@ impl CodeActionsMenu {
this.text_color(colors.text_accent)
}),
)
}),
})
.when_some(action.as_debug_scenario(), |this, scenario| {
this.child(
h_flex()
.overflow_hidden()
.child(scenario.label.clone())
.when(selected, |this| {
this.text_color(colors.text_accent)
}),
)
})
.on_click(cx.listener(move |editor, _, window, cx| {
cx.stop_propagation();
if let Some(task) = editor.confirm_code_action(
&ConfirmCodeAction {
item_ix: Some(item_ix),
},
window,
cx,
) {
task.detach_and_log_err(cx)
}
})),
)
})
.collect()
@ -1128,6 +1120,7 @@ impl CodeActionsMenu {
CodeActionsItem::CodeAction { action, .. } => {
action.lsp_action.title().chars().count()
}
CodeActionsItem::DebugScenario(scenario) => scenario.label.chars().count(),
})
.map(|(ix, _)| ix),
)

View file

@ -5089,6 +5089,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let quick_launch = action.quick_launch;
let mut context_menu = self.context_menu.borrow_mut();
if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
if code_actions.deployed_from_indicator == action.deployed_from_indicator {
@ -5162,8 +5163,6 @@ impl Editor {
Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
});
let debugger_flag = cx.has_flag::<DebuggerFeatureFlag>();
Some(cx.spawn_in(window, async move |editor, cx| {
let task_context = match task_context {
Some(task_context) => task_context.await,
@ -5171,7 +5170,7 @@ impl Editor {
};
let resolved_tasks =
tasks
.zip(task_context)
.zip(task_context.clone())
.map(|(tasks, task_context)| ResolvedTasks {
templates: tasks.resolve(&task_context).collect(),
position: snapshot.buffer_snapshot.anchor_before(Point::new(
@ -5179,22 +5178,49 @@ impl Editor {
tasks.column,
)),
});
let spawn_straight_away = resolved_tasks.as_ref().map_or(false, |tasks| {
tasks
.templates
.iter()
.filter(|task| {
if matches!(task.1.task_type(), task::TaskType::Debug(_)) {
debugger_flag
} else {
true
}
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());
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 debug_adapter: SharedString = buffer
.read(cx)
.language()?
.context_provider()?
.debug_adapter()?
.into();
dap_store.update(cx, |this, cx| {
for (_, task) in &resolved_tasks.templates {
if let Some(scenario) = this
.debug_scenario_for_build_task(
task.resolved.clone(),
SharedString::from(
task.original_task().label.clone(),
),
debug_adapter.clone(),
cx,
)
{
scenarios.push(scenario);
}
}
});
Some(scenarios)
})
.count()
== 1
}) && code_actions
.as_ref()
.map_or(true, |actions| actions.is_empty());
.unwrap_or_default()
} else {
vec![]
}
})?;
if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
*editor.context_menu.borrow_mut() =
Some(CodeContextMenu::CodeActions(CodeActionsMenu {
@ -5202,7 +5228,8 @@ impl Editor {
actions: CodeActionContents::new(
resolved_tasks,
code_actions,
cx,
debug_scenarios,
task_context.unwrap_or_default(),
),
selected_item: Default::default(),
scroll_handle: UniformListScrollHandle::default(),
@ -5262,25 +5289,17 @@ impl Editor {
match action {
CodeActionsItem::Task(task_source_kind, resolved_task) => {
match resolved_task.task_type() {
task::TaskType::Script => workspace.update(cx, |workspace, cx| {
workspace.schedule_resolved_task(
task_source_kind,
resolved_task,
false,
window,
cx,
);
workspace.update(cx, |workspace, cx| {
workspace.schedule_resolved_task(
task_source_kind,
resolved_task,
false,
window,
cx,
);
Some(Task::ready(Ok(())))
}),
task::TaskType::Debug(_) => {
workspace.update(cx, |workspace, cx| {
workspace.schedule_debug_task(resolved_task, window, cx);
});
Some(Task::ready(Ok(())))
}
}
Some(Task::ready(Ok(())))
})
}
CodeActionsItem::CodeAction {
excerpt_id,
@ -5302,6 +5321,14 @@ impl Editor {
.await
}))
}
CodeActionsItem::DebugScenario(scenario) => {
let context = actions_menu.actions.context.clone();
workspace.update(cx, |workspace, cx| {
workspace.start_debug_session(scenario, context, Some(buffer), window, cx);
});
Some(Task::ready(Ok(())))
}
}
}
@ -6660,6 +6687,7 @@ impl Editor {
"Toggle Code Actions",
&ToggleCodeActions {
deployed_from_indicator: None,
quick_launch: false,
},
&focus_handle,
window,
@ -6668,11 +6696,13 @@ impl Editor {
}
})
})
.on_click(cx.listener(move |editor, _e, window, cx| {
.on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
let quick_launch = e.down.button == MouseButton::Left;
window.focus(&editor.focus_handle(cx));
editor.toggle_code_actions(
&ToggleCodeActions {
deployed_from_indicator: Some(row),
quick_launch,
},
window,
cx,
@ -7050,7 +7080,7 @@ impl Editor {
let context = task_context.await?;
let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
let resolved = resolved_task.resolved.as_mut()?;
let resolved = &mut resolved_task.resolved;
resolved.reveal = reveal_strategy;
workspace
@ -7140,11 +7170,13 @@ impl Editor {
.icon_size(IconSize::XSmall)
.icon_color(color)
.toggle_state(is_active)
.on_click(cx.listener(move |editor, _e, window, cx| {
.on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
let quick_launch = e.down.button == MouseButton::Left;
window.focus(&editor.focus_handle(cx));
editor.toggle_code_actions(
&ToggleCodeActions {
deployed_from_indicator: Some(row),
quick_launch,
},
window,
cx,

View file

@ -211,6 +211,7 @@ pub fn deploy_context_menu(
"Show Code Actions",
Box::new(ToggleCodeActions {
deployed_from_indicator: None,
quick_launch: false,
}),
)
.separator()