Show tasks in debugger: start (#30584)
- **Show relevant tasks in debugger: start** - **Add history too** Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: Cole <cole@zed.dev> Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
parent
7eb226b3fc
commit
1fd8fbe6d1
8 changed files with 272 additions and 159 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4167,6 +4167,7 @@ dependencies = [
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
|
"file_icons",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
|
|
@ -36,6 +36,7 @@ dap_adapters = { workspace = true, optional = true }
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
|
file_icons.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
|
@ -218,6 +218,18 @@ impl DebugPanel {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
if let Some(inventory) = self
|
||||||
|
.project
|
||||||
|
.read(cx)
|
||||||
|
.task_store()
|
||||||
|
.read(cx)
|
||||||
|
.task_inventory()
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
inventory.update(cx, |inventory, _| {
|
||||||
|
inventory.scenario_scheduled(scenario.clone());
|
||||||
|
})
|
||||||
|
}
|
||||||
let task = cx.spawn_in(window, {
|
let task = cx.spawn_in(window, {
|
||||||
let session = session.clone();
|
let session = session.clone();
|
||||||
async move |this, cx| {
|
async move |this, cx| {
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ops::Not,
|
ops::Not,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
usize,
|
usize,
|
||||||
};
|
};
|
||||||
|
@ -50,7 +49,6 @@ pub(super) struct NewSessionModal {
|
||||||
attach_mode: Entity<AttachMode>,
|
attach_mode: Entity<AttachMode>,
|
||||||
custom_mode: Entity<CustomMode>,
|
custom_mode: Entity<CustomMode>,
|
||||||
debugger: Option<DebugAdapterName>,
|
debugger: Option<DebugAdapterName>,
|
||||||
task_contexts: Arc<TaskContexts>,
|
|
||||||
save_scenario_state: Option<SaveScenarioState>,
|
save_scenario_state: Option<SaveScenarioState>,
|
||||||
_subscriptions: [Subscription; 2],
|
_subscriptions: [Subscription; 2],
|
||||||
}
|
}
|
||||||
|
@ -85,14 +83,6 @@ impl NewSessionModal {
|
||||||
let task_store = workspace.project().read(cx).task_store().clone();
|
let task_store = workspace.project().read(cx).task_store().clone();
|
||||||
|
|
||||||
cx.spawn_in(window, async move |workspace, cx| {
|
cx.spawn_in(window, async move |workspace, cx| {
|
||||||
let task_contexts = Arc::from(
|
|
||||||
workspace
|
|
||||||
.update_in(cx, |workspace, window, cx| {
|
|
||||||
tasks_ui::task_contexts(workspace, window, cx)
|
|
||||||
})?
|
|
||||||
.await,
|
|
||||||
);
|
|
||||||
|
|
||||||
workspace.update_in(cx, |workspace, window, cx| {
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
let workspace_handle = workspace.weak_handle();
|
let workspace_handle = workspace.weak_handle();
|
||||||
workspace.toggle_modal(window, cx, |window, cx| {
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
|
@ -100,12 +90,7 @@ impl NewSessionModal {
|
||||||
|
|
||||||
let launch_picker = cx.new(|cx| {
|
let launch_picker = cx.new(|cx| {
|
||||||
Picker::uniform_list(
|
Picker::uniform_list(
|
||||||
DebugScenarioDelegate::new(
|
DebugScenarioDelegate::new(debug_panel.downgrade(), task_store),
|
||||||
debug_panel.downgrade(),
|
|
||||||
workspace_handle.clone(),
|
|
||||||
task_store,
|
|
||||||
task_contexts.clone(),
|
|
||||||
),
|
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -124,11 +109,38 @@ impl NewSessionModal {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
let active_cwd = task_contexts
|
let custom_mode = CustomMode::new(None, window, cx);
|
||||||
.active_context()
|
|
||||||
.and_then(|context| context.cwd.clone());
|
|
||||||
|
|
||||||
let custom_mode = CustomMode::new(None, active_cwd, window, cx);
|
cx.spawn_in(window, {
|
||||||
|
let workspace_handle = workspace_handle.clone();
|
||||||
|
async move |this, cx| {
|
||||||
|
let task_contexts = workspace_handle
|
||||||
|
.update_in(cx, |workspace, window, cx| {
|
||||||
|
tasks_ui::task_contexts(workspace, window, cx)
|
||||||
|
})?
|
||||||
|
.await;
|
||||||
|
|
||||||
|
this.update_in(cx, |this, window, cx| {
|
||||||
|
if let Some(active_cwd) = task_contexts
|
||||||
|
.active_context()
|
||||||
|
.and_then(|context| context.cwd.clone())
|
||||||
|
{
|
||||||
|
this.custom_mode.update(cx, |custom, cx| {
|
||||||
|
custom.load(active_cwd, window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.launch_picker.update(cx, |picker, cx| {
|
||||||
|
picker
|
||||||
|
.delegate
|
||||||
|
.task_contexts_loaded(task_contexts, window, cx);
|
||||||
|
picker.refresh(window, cx);
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
launch_picker,
|
launch_picker,
|
||||||
|
@ -138,7 +150,6 @@ impl NewSessionModal {
|
||||||
mode: NewSessionMode::Launch,
|
mode: NewSessionMode::Launch,
|
||||||
debug_panel: debug_panel.downgrade(),
|
debug_panel: debug_panel.downgrade(),
|
||||||
workspace: workspace_handle,
|
workspace: workspace_handle,
|
||||||
task_contexts,
|
|
||||||
save_scenario_state: None,
|
save_scenario_state: None,
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
}
|
}
|
||||||
|
@ -205,8 +216,6 @@ impl NewSessionModal {
|
||||||
|
|
||||||
fn start_new_session(&self, window: &mut Window, cx: &mut Context<Self>) {
|
fn start_new_session(&self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let Some(debugger) = self.debugger.as_ref() else {
|
let Some(debugger) = self.debugger.as_ref() else {
|
||||||
// todo(debugger): show in UI.
|
|
||||||
log::error!("No debugger selected");
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -223,10 +232,12 @@ impl NewSessionModal {
|
||||||
};
|
};
|
||||||
|
|
||||||
let debug_panel = self.debug_panel.clone();
|
let debug_panel = self.debug_panel.clone();
|
||||||
let task_contexts = self.task_contexts.clone();
|
let Some(task_contexts) = self.task_contexts(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let task_context = task_contexts.active_context().cloned().unwrap_or_default();
|
||||||
|
let worktree_id = task_contexts.worktree();
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let task_context = task_contexts.active_context().cloned().unwrap_or_default();
|
|
||||||
let worktree_id = task_contexts.worktree();
|
|
||||||
debug_panel.update_in(cx, |debug_panel, window, cx| {
|
debug_panel.update_in(cx, |debug_panel, window, cx| {
|
||||||
debug_panel.start_session(config, task_context, None, worktree_id, window, cx)
|
debug_panel.start_session(config, task_context, None, worktree_id, window, cx)
|
||||||
})?;
|
})?;
|
||||||
|
@ -260,6 +271,11 @@ impl NewSessionModal {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn task_contexts<'a>(&self, cx: &'a mut Context<Self>) -> Option<&'a TaskContexts> {
|
||||||
|
self.launch_picker.read(cx).delegate.task_contexts.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn adapter_drop_down_menu(
|
fn adapter_drop_down_menu(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -267,15 +283,14 @@ impl NewSessionModal {
|
||||||
) -> ui::DropdownMenu {
|
) -> ui::DropdownMenu {
|
||||||
let workspace = self.workspace.clone();
|
let workspace = self.workspace.clone();
|
||||||
let weak = cx.weak_entity();
|
let weak = cx.weak_entity();
|
||||||
let active_buffer_language = self
|
let active_buffer = self.task_contexts(cx).and_then(|tc| {
|
||||||
.task_contexts
|
tc.active_item_context
|
||||||
.active_item_context
|
.as_ref()
|
||||||
.as_ref()
|
.and_then(|aic| aic.1.as_ref().map(|l| l.buffer.clone()))
|
||||||
.and_then(|item| {
|
});
|
||||||
item.1
|
|
||||||
.as_ref()
|
let active_buffer_language = active_buffer
|
||||||
.and_then(|location| location.buffer.read(cx).language())
|
.and_then(|buffer| buffer.read(cx).language())
|
||||||
})
|
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
let mut available_adapters = workspace
|
let mut available_adapters = workspace
|
||||||
|
@ -515,7 +530,10 @@ impl Render for NewSessionModal {
|
||||||
.debugger
|
.debugger
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|debugger| this.debug_scenario(&debugger, cx))
|
.and_then(|debugger| this.debug_scenario(&debugger, cx))
|
||||||
.zip(this.task_contexts.worktree())
|
.zip(
|
||||||
|
this.task_contexts(cx)
|
||||||
|
.and_then(|tcx| tcx.worktree()),
|
||||||
|
)
|
||||||
.and_then(|(scenario, worktree_id)| {
|
.and_then(|(scenario, worktree_id)| {
|
||||||
this.debug_panel
|
this.debug_panel
|
||||||
.update(cx, |panel, cx| {
|
.update(cx, |panel, cx| {
|
||||||
|
@ -715,13 +733,12 @@ pub(super) struct CustomMode {
|
||||||
impl CustomMode {
|
impl CustomMode {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
past_launch_config: Option<LaunchRequest>,
|
past_launch_config: Option<LaunchRequest>,
|
||||||
active_cwd: Option<PathBuf>,
|
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Entity<Self> {
|
) -> Entity<Self> {
|
||||||
let (past_program, past_cwd) = past_launch_config
|
let (past_program, past_cwd) = past_launch_config
|
||||||
.map(|config| (Some(config.program), config.cwd))
|
.map(|config| (Some(config.program), config.cwd))
|
||||||
.unwrap_or_else(|| (None, active_cwd));
|
.unwrap_or_else(|| (None, None));
|
||||||
|
|
||||||
let program = cx.new(|cx| Editor::single_line(window, cx));
|
let program = cx.new(|cx| Editor::single_line(window, cx));
|
||||||
program.update(cx, |this, cx| {
|
program.update(cx, |this, cx| {
|
||||||
|
@ -745,6 +762,14 @@ impl CustomMode {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load(&mut self, cwd: PathBuf, window: &mut Window, cx: &mut App) {
|
||||||
|
self.cwd.update(cx, |editor, cx| {
|
||||||
|
if editor.is_empty(cx) {
|
||||||
|
editor.set_text(cwd.to_string_lossy(), window, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn debug_request(&self, cx: &App) -> task::LaunchRequest {
|
pub(super) fn debug_request(&self, cx: &App) -> task::LaunchRequest {
|
||||||
let path = self.cwd.read(cx).text(cx);
|
let path = self.cwd.read(cx).text(cx);
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
|
@ -894,33 +919,64 @@ impl AttachMode {
|
||||||
|
|
||||||
pub(super) struct DebugScenarioDelegate {
|
pub(super) struct DebugScenarioDelegate {
|
||||||
task_store: Entity<TaskStore>,
|
task_store: Entity<TaskStore>,
|
||||||
candidates: Option<Vec<(TaskSourceKind, DebugScenario)>>,
|
candidates: Vec<(Option<TaskSourceKind>, DebugScenario)>,
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
matches: Vec<StringMatch>,
|
matches: Vec<StringMatch>,
|
||||||
prompt: String,
|
prompt: String,
|
||||||
debug_panel: WeakEntity<DebugPanel>,
|
debug_panel: WeakEntity<DebugPanel>,
|
||||||
workspace: WeakEntity<Workspace>,
|
task_contexts: Option<TaskContexts>,
|
||||||
task_contexts: Arc<TaskContexts>,
|
divider_index: Option<usize>,
|
||||||
|
last_used_candidate_index: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugScenarioDelegate {
|
impl DebugScenarioDelegate {
|
||||||
pub(super) fn new(
|
pub(super) fn new(debug_panel: WeakEntity<DebugPanel>, task_store: Entity<TaskStore>) -> Self {
|
||||||
debug_panel: WeakEntity<DebugPanel>,
|
|
||||||
workspace: WeakEntity<Workspace>,
|
|
||||||
task_store: Entity<TaskStore>,
|
|
||||||
task_contexts: Arc<TaskContexts>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
task_store,
|
task_store,
|
||||||
candidates: None,
|
candidates: Vec::default(),
|
||||||
selected_index: 0,
|
selected_index: 0,
|
||||||
matches: Vec::new(),
|
matches: Vec::new(),
|
||||||
prompt: String::new(),
|
prompt: String::new(),
|
||||||
debug_panel,
|
debug_panel,
|
||||||
workspace,
|
task_contexts: None,
|
||||||
task_contexts,
|
divider_index: None,
|
||||||
|
last_used_candidate_index: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn task_contexts_loaded(
|
||||||
|
&mut self,
|
||||||
|
task_contexts: TaskContexts,
|
||||||
|
_window: &mut Window,
|
||||||
|
cx: &mut Context<Picker<Self>>,
|
||||||
|
) {
|
||||||
|
self.task_contexts = Some(task_contexts);
|
||||||
|
|
||||||
|
let (recent, scenarios) = self
|
||||||
|
.task_store
|
||||||
|
.update(cx, |task_store, cx| {
|
||||||
|
task_store.task_inventory().map(|inventory| {
|
||||||
|
inventory.update(cx, |inventory, cx| {
|
||||||
|
inventory.list_debug_scenarios(self.task_contexts.as_ref().unwrap(), cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !recent.is_empty() {
|
||||||
|
self.last_used_candidate_index = Some(recent.len() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.candidates = recent
|
||||||
|
.into_iter()
|
||||||
|
.map(|scenario| (None, scenario))
|
||||||
|
.chain(
|
||||||
|
scenarios
|
||||||
|
.into_iter()
|
||||||
|
.map(|(kind, scenario)| (Some(kind), scenario)),
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerDelegate for DebugScenarioDelegate {
|
impl PickerDelegate for DebugScenarioDelegate {
|
||||||
|
@ -954,53 +1010,15 @@ impl PickerDelegate for DebugScenarioDelegate {
|
||||||
cx: &mut Context<picker::Picker<Self>>,
|
cx: &mut Context<picker::Picker<Self>>,
|
||||||
) -> gpui::Task<()> {
|
) -> gpui::Task<()> {
|
||||||
let candidates = self.candidates.clone();
|
let candidates = self.candidates.clone();
|
||||||
let workspace = self.workspace.clone();
|
|
||||||
let task_store = self.task_store.clone();
|
|
||||||
|
|
||||||
cx.spawn_in(window, async move |picker, cx| {
|
cx.spawn_in(window, async move |picker, cx| {
|
||||||
let candidates: Vec<_> = match &candidates {
|
let candidates: Vec<_> = candidates
|
||||||
Some(candidates) => candidates
|
.into_iter()
|
||||||
.into_iter()
|
.enumerate()
|
||||||
.enumerate()
|
.map(|(index, (_, candidate))| {
|
||||||
.map(|(index, (_, candidate))| {
|
StringMatchCandidate::new(index, candidate.label.as_ref())
|
||||||
StringMatchCandidate::new(index, candidate.label.as_ref())
|
})
|
||||||
})
|
.collect();
|
||||||
.collect(),
|
|
||||||
None => {
|
|
||||||
let worktree_ids: Vec<_> = workspace
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
this.visible_worktrees(cx)
|
|
||||||
.map(|tree| tree.read(cx).id())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let scenarios: Vec<_> = task_store
|
|
||||||
.update(cx, |task_store, cx| {
|
|
||||||
task_store.task_inventory().map(|item| {
|
|
||||||
item.read(cx).list_debug_scenarios(worktree_ids.into_iter())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
picker
|
|
||||||
.update(cx, |picker, _| {
|
|
||||||
picker.delegate.candidates = Some(scenarios.clone());
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
scenarios
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, (_, candidate))| {
|
|
||||||
StringMatchCandidate::new(index, candidate.label.as_ref())
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let matches = fuzzy::match_strings(
|
let matches = fuzzy::match_strings(
|
||||||
&candidates,
|
&candidates,
|
||||||
|
@ -1019,6 +1037,13 @@ impl PickerDelegate for DebugScenarioDelegate {
|
||||||
delegate.matches = matches;
|
delegate.matches = matches;
|
||||||
delegate.prompt = query;
|
delegate.prompt = query;
|
||||||
|
|
||||||
|
delegate.divider_index = delegate.last_used_candidate_index.and_then(|index| {
|
||||||
|
let index = delegate
|
||||||
|
.matches
|
||||||
|
.partition_point(|matching_task| matching_task.candidate_id <= index);
|
||||||
|
Some(index).and_then(|index| (index != 0).then(|| index - 1))
|
||||||
|
});
|
||||||
|
|
||||||
if delegate.matches.is_empty() {
|
if delegate.matches.is_empty() {
|
||||||
delegate.selected_index = 0;
|
delegate.selected_index = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1030,34 +1055,34 @@ impl PickerDelegate for DebugScenarioDelegate {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn separators_after_indices(&self) -> Vec<usize> {
|
||||||
|
if let Some(i) = self.divider_index {
|
||||||
|
vec![i]
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
|
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
|
||||||
let debug_scenario = self
|
let debug_scenario = self
|
||||||
.matches
|
.matches
|
||||||
.get(self.selected_index())
|
.get(self.selected_index())
|
||||||
.and_then(|match_candidate| {
|
.and_then(|match_candidate| self.candidates.get(match_candidate.candidate_id).cloned());
|
||||||
self.candidates
|
|
||||||
.as_ref()
|
|
||||||
.map(|candidates| candidates[match_candidate.candidate_id].clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
let Some((task_source_kind, debug_scenario)) = debug_scenario else {
|
let Some((_, debug_scenario)) = debug_scenario else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (task_context, worktree_id) = if let TaskSourceKind::Worktree {
|
let (task_context, worktree_id) = self
|
||||||
id: worktree_id,
|
.task_contexts
|
||||||
directory_in_worktree: _,
|
.as_ref()
|
||||||
id_base: _,
|
.and_then(|task_contexts| {
|
||||||
} = task_source_kind
|
Some((
|
||||||
{
|
task_contexts.active_context().cloned()?,
|
||||||
self.task_contexts
|
task_contexts.worktree(),
|
||||||
.task_context_for_worktree_id(worktree_id)
|
))
|
||||||
.cloned()
|
})
|
||||||
.map(|context| (context, Some(worktree_id)))
|
.unwrap_or_default();
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
self.debug_panel
|
self.debug_panel
|
||||||
.update(cx, |panel, cx| {
|
.update(cx, |panel, cx| {
|
||||||
|
@ -1087,10 +1112,19 @@ impl PickerDelegate for DebugScenarioDelegate {
|
||||||
char_count: hit.string.chars().count(),
|
char_count: hit.string.chars().count(),
|
||||||
color: Color::Default,
|
color: Color::Default,
|
||||||
};
|
};
|
||||||
|
let task_kind = &self.candidates[hit.candidate_id].0;
|
||||||
|
|
||||||
let icon = Icon::new(IconName::FileTree)
|
let icon = match task_kind {
|
||||||
.color(Color::Muted)
|
Some(TaskSourceKind::Lsp(..)) => Some(Icon::new(IconName::Bolt)),
|
||||||
.size(ui::IconSize::Small);
|
Some(TaskSourceKind::UserInput) => Some(Icon::new(IconName::Terminal)),
|
||||||
|
Some(TaskSourceKind::AbsPath { .. }) => Some(Icon::new(IconName::Settings)),
|
||||||
|
Some(TaskSourceKind::Worktree { .. }) => Some(Icon::new(IconName::FileTree)),
|
||||||
|
Some(TaskSourceKind::Language { name }) => file_icons::FileIcons::get(cx)
|
||||||
|
.get_icon_for_type(&name.to_lowercase(), cx)
|
||||||
|
.map(Icon::from_path),
|
||||||
|
None => Some(Icon::new(IconName::HistoryRerun)),
|
||||||
|
}
|
||||||
|
.map(|icon| icon.color(Color::Muted).size(ui::IconSize::Small));
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
ListItem::new(SharedString::from(format!("debug-scenario-selection-{ix}")))
|
ListItem::new(SharedString::from(format!("debug-scenario-selection-{ix}")))
|
||||||
|
|
|
@ -1103,6 +1103,7 @@ impl CodeActionsMenu {
|
||||||
this.child(
|
this.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
|
.child("debug: ")
|
||||||
.child(scenario.label.clone())
|
.child(scenario.label.clone())
|
||||||
.when(selected, |this| {
|
.when(selected, |this| {
|
||||||
this.text_color(colors.text_accent)
|
this.text_color(colors.text_accent)
|
||||||
|
@ -1138,7 +1139,9 @@ impl CodeActionsMenu {
|
||||||
CodeActionsItem::CodeAction { action, .. } => {
|
CodeActionsItem::CodeAction { action, .. } => {
|
||||||
action.lsp_action.title().chars().count()
|
action.lsp_action.title().chars().count()
|
||||||
}
|
}
|
||||||
CodeActionsItem::DebugScenario(scenario) => scenario.label.chars().count(),
|
CodeActionsItem::DebugScenario(scenario) => {
|
||||||
|
format!("debug: {}", scenario.label).chars().count()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map(|(ix, _)| ix),
|
.map(|(ix, _)| ix),
|
||||||
)
|
)
|
||||||
|
|
|
@ -5331,9 +5331,9 @@ impl Editor {
|
||||||
.map(SharedString::from)
|
.map(SharedString::from)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
dap_store.update(cx, |this, cx| {
|
dap_store.update(cx, |dap_store, cx| {
|
||||||
for (_, task) in &resolved_tasks.templates {
|
for (_, task) in &resolved_tasks.templates {
|
||||||
if let Some(scenario) = this
|
if let Some(scenario) = dap_store
|
||||||
.debug_scenario_for_build_task(
|
.debug_scenario_for_build_task(
|
||||||
task.original_task().clone(),
|
task.original_task().clone(),
|
||||||
debug_adapter.clone().into(),
|
debug_adapter.clone().into(),
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl DapLocator for CargoLocator {
|
||||||
}
|
}
|
||||||
let mut task_template = build_config.clone();
|
let mut task_template = build_config.clone();
|
||||||
let cargo_action = task_template.args.first_mut()?;
|
let cargo_action = task_template.args.first_mut()?;
|
||||||
if cargo_action == "check" {
|
if cargo_action == "check" || cargo_action == "clean" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +75,9 @@ impl DapLocator for CargoLocator {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let label = format!("Debug `{resolved_label}`");
|
|
||||||
Some(DebugScenario {
|
Some(DebugScenario {
|
||||||
adapter: adapter.0,
|
adapter: adapter.0,
|
||||||
label: SharedString::from(label),
|
label: resolved_label.to_string().into(),
|
||||||
build: Some(BuildTaskDefinition::Template {
|
build: Some(BuildTaskDefinition::Template {
|
||||||
task_template,
|
task_template,
|
||||||
locator_name: Some(self.name()),
|
locator_name: Some(self.name()),
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::{
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
|
use dap::DapRegistry;
|
||||||
use gpui::{App, AppContext as _, Entity, SharedString, Task};
|
use gpui::{App, AppContext as _, Entity, SharedString, Task};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -33,6 +34,7 @@ use crate::{task_store::TaskSettingsLocation, worktree_store::WorktreeStore};
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
last_scheduled_tasks: VecDeque<(TaskSourceKind, ResolvedTask)>,
|
last_scheduled_tasks: VecDeque<(TaskSourceKind, ResolvedTask)>,
|
||||||
|
last_scheduled_scenarios: VecDeque<DebugScenario>,
|
||||||
templates_from_settings: InventoryFor<TaskTemplate>,
|
templates_from_settings: InventoryFor<TaskTemplate>,
|
||||||
scenarios_from_settings: InventoryFor<DebugScenario>,
|
scenarios_from_settings: InventoryFor<DebugScenario>,
|
||||||
}
|
}
|
||||||
|
@ -63,30 +65,28 @@ struct InventoryFor<T> {
|
||||||
impl<T: InventoryContents> InventoryFor<T> {
|
impl<T: InventoryContents> InventoryFor<T> {
|
||||||
fn worktree_scenarios(
|
fn worktree_scenarios(
|
||||||
&self,
|
&self,
|
||||||
worktree: Option<WorktreeId>,
|
worktree: WorktreeId,
|
||||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
||||||
worktree.into_iter().flat_map(|worktree| {
|
self.worktree
|
||||||
self.worktree
|
.get(&worktree)
|
||||||
.get(&worktree)
|
.into_iter()
|
||||||
.into_iter()
|
.flatten()
|
||||||
.flatten()
|
.flat_map(|(directory, templates)| {
|
||||||
.flat_map(|(directory, templates)| {
|
templates.iter().map(move |template| (directory, template))
|
||||||
templates.iter().map(move |template| (directory, template))
|
})
|
||||||
})
|
.map(move |(directory, template)| {
|
||||||
.map(move |(directory, template)| {
|
(
|
||||||
(
|
TaskSourceKind::Worktree {
|
||||||
TaskSourceKind::Worktree {
|
id: worktree,
|
||||||
id: worktree,
|
directory_in_worktree: directory.to_path_buf(),
|
||||||
directory_in_worktree: directory.to_path_buf(),
|
id_base: Cow::Owned(format!(
|
||||||
id_base: Cow::Owned(format!(
|
"local worktree {} from directory {directory:?}",
|
||||||
"local worktree {} from directory {directory:?}",
|
T::LABEL
|
||||||
T::LABEL
|
)),
|
||||||
)),
|
},
|
||||||
},
|
template.clone(),
|
||||||
template.clone(),
|
)
|
||||||
)
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_scenarios(&self) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
fn global_scenarios(&self) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
||||||
|
@ -168,6 +168,13 @@ impl TaskContexts {
|
||||||
.and_then(|(_, location, _)| location.as_ref())
|
.and_then(|(_, location, _)| location.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file(&self, cx: &App) -> Option<Arc<dyn File>> {
|
||||||
|
self.active_item_context
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(_, location, _)| location.as_ref())
|
||||||
|
.and_then(|location| location.buffer.read(cx).file().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn worktree(&self) -> Option<WorktreeId> {
|
pub fn worktree(&self) -> Option<WorktreeId> {
|
||||||
self.active_item_context
|
self.active_item_context
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -214,16 +221,69 @@ impl Inventory {
|
||||||
cx.new(|_| Self::default())
|
cx.new(|_| Self::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scenario_scheduled(&mut self, scenario: DebugScenario) {
|
||||||
|
self.last_scheduled_scenarios
|
||||||
|
.retain(|s| s.label != scenario.label);
|
||||||
|
self.last_scheduled_scenarios.push_back(scenario);
|
||||||
|
if self.last_scheduled_scenarios.len() > 5_000 {
|
||||||
|
self.last_scheduled_scenarios.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_debug_scenarios(
|
pub fn list_debug_scenarios(
|
||||||
&self,
|
&self,
|
||||||
worktrees: impl Iterator<Item = WorktreeId>,
|
task_contexts: &TaskContexts,
|
||||||
) -> Vec<(TaskSourceKind, DebugScenario)> {
|
cx: &mut App,
|
||||||
let global_scenarios = self.global_debug_scenarios_from_settings();
|
) -> (Vec<DebugScenario>, Vec<(TaskSourceKind, DebugScenario)>) {
|
||||||
|
let mut scenarios = Vec::new();
|
||||||
|
|
||||||
worktrees
|
if let Some(worktree_id) = task_contexts
|
||||||
.flat_map(|tree_id| self.worktree_scenarios_from_settings(Some(tree_id)))
|
.active_worktree_context
|
||||||
.chain(global_scenarios)
|
.iter()
|
||||||
.collect()
|
.chain(task_contexts.other_worktree_contexts.iter())
|
||||||
|
.map(|context| context.0)
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
scenarios.extend(self.worktree_scenarios_from_settings(worktree_id));
|
||||||
|
}
|
||||||
|
scenarios.extend(self.global_debug_scenarios_from_settings());
|
||||||
|
|
||||||
|
let (_, new) = self.used_and_current_resolved_tasks(task_contexts, cx);
|
||||||
|
if let Some(location) = task_contexts.location() {
|
||||||
|
let file = location.buffer.read(cx).file();
|
||||||
|
let language = location.buffer.read(cx).language();
|
||||||
|
let language_name = language.as_ref().map(|l| l.name());
|
||||||
|
let adapter = language_settings(language_name, file, cx)
|
||||||
|
.debuggers
|
||||||
|
.first()
|
||||||
|
.map(SharedString::from)
|
||||||
|
.or_else(|| {
|
||||||
|
language.and_then(|l| l.config().debuggers.first().map(SharedString::from))
|
||||||
|
});
|
||||||
|
if let Some(adapter) = adapter {
|
||||||
|
for (kind, task) in new {
|
||||||
|
if let Some(scenario) =
|
||||||
|
DapRegistry::global(cx)
|
||||||
|
.locators()
|
||||||
|
.values()
|
||||||
|
.find_map(|locator| {
|
||||||
|
locator.create_scenario(
|
||||||
|
&task.original_task().clone(),
|
||||||
|
&task.display_label(),
|
||||||
|
adapter.clone().into(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
scenarios.push((kind, scenario));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
self.last_scheduled_scenarios.iter().cloned().collect(),
|
||||||
|
scenarios,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn task_template_by_label(
|
pub fn task_template_by_label(
|
||||||
|
@ -262,7 +322,9 @@ impl Inventory {
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
||||||
let global_tasks = self.global_templates_from_settings();
|
let global_tasks = self.global_templates_from_settings();
|
||||||
let worktree_tasks = self.worktree_templates_from_settings(worktree);
|
let worktree_tasks = worktree
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|worktree| self.worktree_templates_from_settings(worktree));
|
||||||
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
||||||
name: language.name().into(),
|
name: language.name().into(),
|
||||||
});
|
});
|
||||||
|
@ -354,8 +416,9 @@ impl Inventory {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|tasks| tasks.0.into_iter())
|
.flat_map(|tasks| tasks.0.into_iter())
|
||||||
.flat_map(|task| Some((task_source_kind.clone()?, task)));
|
.flat_map(|task| Some((task_source_kind.clone()?, task)));
|
||||||
let worktree_tasks = self
|
let worktree_tasks = worktree
|
||||||
.worktree_templates_from_settings(worktree)
|
.into_iter()
|
||||||
|
.flat_map(|worktree| self.worktree_templates_from_settings(worktree))
|
||||||
.chain(language_tasks)
|
.chain(language_tasks)
|
||||||
.chain(global_tasks);
|
.chain(global_tasks);
|
||||||
|
|
||||||
|
@ -471,14 +534,14 @@ impl Inventory {
|
||||||
|
|
||||||
fn worktree_scenarios_from_settings(
|
fn worktree_scenarios_from_settings(
|
||||||
&self,
|
&self,
|
||||||
worktree: Option<WorktreeId>,
|
worktree: WorktreeId,
|
||||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, DebugScenario)> {
|
) -> impl '_ + Iterator<Item = (TaskSourceKind, DebugScenario)> {
|
||||||
self.scenarios_from_settings.worktree_scenarios(worktree)
|
self.scenarios_from_settings.worktree_scenarios(worktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worktree_templates_from_settings(
|
fn worktree_templates_from_settings(
|
||||||
&self,
|
&self,
|
||||||
worktree: Option<WorktreeId>,
|
worktree: WorktreeId,
|
||||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, TaskTemplate)> {
|
) -> impl '_ + Iterator<Item = (TaskSourceKind, TaskTemplate)> {
|
||||||
self.templates_from_settings.worktree_scenarios(worktree)
|
self.templates_from_settings.worktree_scenarios(worktree)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue