debugger: Run locators on LSP tasks for the new process modal (#32097)
- [x] pass LSP tasks into list_debug_scenarios - [x] load LSP tasks only once for both modals - [x] align ordering - [x] improve appearance of LSP debug task icons - [ ] reconsider how `add_current_language_tasks` works - [ ] add a test Release Notes: - Debugger Beta: Added debuggable LSP tasks to the "Debug" tab of the new process modal. --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
parent
8730d317a8
commit
f36143a461
5 changed files with 135 additions and 22 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4234,6 +4234,7 @@ dependencies = [
|
|||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"itertools 0.14.0",
|
||||
"language",
|
||||
"log",
|
||||
"menu",
|
||||
|
|
|
@ -39,6 +39,7 @@ file_icons.workspace = true
|
|||
futures.workspace = true
|
||||
fuzzy.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
menu.workspace = true
|
||||
|
|
|
@ -19,6 +19,7 @@ use gpui::{
|
|||
InteractiveText, KeyContext, PromptButton, PromptLevel, Render, StyledText, Subscription,
|
||||
TextStyle, UnderlineStyle, WeakEntity,
|
||||
};
|
||||
use itertools::Itertools as _;
|
||||
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
|
||||
use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore};
|
||||
use settings::{Settings, initial_local_debug_tasks_content};
|
||||
|
@ -49,7 +50,7 @@ pub(super) struct NewProcessModal {
|
|||
mode: NewProcessMode,
|
||||
debug_picker: Entity<Picker<DebugDelegate>>,
|
||||
attach_mode: Entity<AttachMode>,
|
||||
launch_mode: Entity<LaunchMode>,
|
||||
launch_mode: Entity<ConfigureMode>,
|
||||
task_mode: TaskMode,
|
||||
debugger: Option<DebugAdapterName>,
|
||||
// save_scenario_state: Option<SaveScenarioState>,
|
||||
|
@ -97,13 +98,13 @@ impl NewProcessModal {
|
|||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
let attach_mode = AttachMode::new(None, workspace_handle.clone(), window, cx);
|
||||
|
||||
let launch_picker = cx.new(|cx| {
|
||||
let debug_picker = cx.new(|cx| {
|
||||
let delegate =
|
||||
DebugDelegate::new(debug_panel.downgrade(), task_store.clone());
|
||||
Picker::uniform_list(delegate, window, cx).modal(false)
|
||||
});
|
||||
|
||||
let configure_mode = LaunchMode::new(window, cx);
|
||||
let configure_mode = ConfigureMode::new(window, cx);
|
||||
|
||||
let task_overrides = Some(TaskOverrides { reveal_target });
|
||||
|
||||
|
@ -122,7 +123,7 @@ impl NewProcessModal {
|
|||
};
|
||||
|
||||
let _subscriptions = [
|
||||
cx.subscribe(&launch_picker, |_, _, _, cx| {
|
||||
cx.subscribe(&debug_picker, |_, _, _, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
}),
|
||||
cx.subscribe(
|
||||
|
@ -137,19 +138,76 @@ impl NewProcessModal {
|
|||
];
|
||||
|
||||
cx.spawn_in(window, {
|
||||
let launch_picker = launch_picker.downgrade();
|
||||
let debug_picker = debug_picker.downgrade();
|
||||
let configure_mode = configure_mode.downgrade();
|
||||
let task_modal = task_mode.task_modal.downgrade();
|
||||
let workspace = workspace_handle.clone();
|
||||
|
||||
async move |this, cx| {
|
||||
let task_contexts = task_contexts.await;
|
||||
let task_contexts = Arc::new(task_contexts);
|
||||
launch_picker
|
||||
let lsp_task_sources = task_contexts.lsp_task_sources.clone();
|
||||
let task_position = task_contexts.latest_selection;
|
||||
// Get LSP tasks and filter out based on language vs lsp preference
|
||||
let (lsp_tasks, prefer_lsp) =
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let lsp_tasks = editor::lsp_tasks(
|
||||
workspace.project().clone(),
|
||||
&lsp_task_sources,
|
||||
task_position,
|
||||
cx,
|
||||
);
|
||||
let prefer_lsp = workspace
|
||||
.active_item(cx)
|
||||
.and_then(|item| item.downcast::<Editor>())
|
||||
.map(|editor| {
|
||||
editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.language_settings(cx)
|
||||
.tasks
|
||||
.prefer_lsp
|
||||
})
|
||||
.unwrap_or(false);
|
||||
(lsp_tasks, prefer_lsp)
|
||||
})?;
|
||||
|
||||
let lsp_tasks = lsp_tasks.await;
|
||||
let add_current_language_tasks = !prefer_lsp || lsp_tasks.is_empty();
|
||||
|
||||
let lsp_tasks = lsp_tasks
|
||||
.into_iter()
|
||||
.flat_map(|(kind, tasks_with_locations)| {
|
||||
tasks_with_locations
|
||||
.into_iter()
|
||||
.sorted_by_key(|(location, task)| {
|
||||
(location.is_none(), task.resolved_label.clone())
|
||||
})
|
||||
.map(move |(_, task)| (kind.clone(), task))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let Some(task_inventory) = task_store
|
||||
.update(cx, |task_store, _| task_store.task_inventory().cloned())?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let (used_tasks, current_resolved_tasks) =
|
||||
task_inventory.update(cx, |task_inventory, cx| {
|
||||
task_inventory
|
||||
.used_and_current_resolved_tasks(&task_contexts, cx)
|
||||
})?;
|
||||
|
||||
debug_picker
|
||||
.update_in(cx, |picker, window, cx| {
|
||||
picker.delegate.task_contexts_loaded(
|
||||
picker.delegate.tasks_loaded(
|
||||
task_contexts.clone(),
|
||||
languages,
|
||||
window,
|
||||
lsp_tasks.clone(),
|
||||
current_resolved_tasks.clone(),
|
||||
add_current_language_tasks,
|
||||
cx,
|
||||
);
|
||||
picker.refresh(window, cx);
|
||||
|
@ -170,7 +228,15 @@ impl NewProcessModal {
|
|||
|
||||
task_modal
|
||||
.update_in(cx, |task_modal, window, cx| {
|
||||
task_modal.task_contexts_loaded(task_contexts, window, cx);
|
||||
task_modal.tasks_loaded(
|
||||
task_contexts,
|
||||
lsp_tasks,
|
||||
used_tasks,
|
||||
current_resolved_tasks,
|
||||
add_current_language_tasks,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
|
||||
|
@ -178,12 +244,14 @@ impl NewProcessModal {
|
|||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
|
||||
anyhow::Ok(())
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {
|
||||
debug_picker: launch_picker,
|
||||
debug_picker,
|
||||
attach_mode,
|
||||
launch_mode: configure_mode,
|
||||
task_mode,
|
||||
|
@ -820,14 +888,14 @@ impl RenderOnce for AttachMode {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct LaunchMode {
|
||||
pub(super) struct ConfigureMode {
|
||||
program: Entity<Editor>,
|
||||
cwd: Entity<Editor>,
|
||||
stop_on_entry: ToggleState,
|
||||
// save_to_debug_json: ToggleState,
|
||||
}
|
||||
|
||||
impl LaunchMode {
|
||||
impl ConfigureMode {
|
||||
pub(super) fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
let program = cx.new(|cx| Editor::single_line(window, cx));
|
||||
program.update(cx, |this, cx| {
|
||||
|
@ -1067,21 +1135,29 @@ impl DebugDelegate {
|
|||
(language, scenario)
|
||||
}
|
||||
|
||||
pub fn task_contexts_loaded(
|
||||
pub fn tasks_loaded(
|
||||
&mut self,
|
||||
task_contexts: Arc<TaskContexts>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
_window: &mut Window,
|
||||
lsp_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
add_current_language_tasks: bool,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) {
|
||||
self.task_contexts = Some(task_contexts);
|
||||
self.task_contexts = Some(task_contexts.clone());
|
||||
|
||||
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)
|
||||
inventory.list_debug_scenarios(
|
||||
&task_contexts,
|
||||
lsp_tasks,
|
||||
current_resolved_tasks,
|
||||
add_current_language_tasks,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1257,12 +1333,17 @@ impl PickerDelegate for DebugDelegate {
|
|||
.map(|icon| icon.color(Color::Muted).size(IconSize::Small));
|
||||
let indicator = if matches!(task_kind, Some(TaskSourceKind::Lsp { .. })) {
|
||||
Some(Indicator::icon(
|
||||
Icon::new(IconName::BoltFilled).color(Color::Muted),
|
||||
Icon::new(IconName::BoltFilled)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let icon = icon.map(|icon| IconWithIndicator::new(icon, indicator));
|
||||
let icon = icon.map(|icon| {
|
||||
IconWithIndicator::new(icon, indicator)
|
||||
.indicator_border_color(Some(cx.theme().colors().border_transparent))
|
||||
});
|
||||
|
||||
Some(
|
||||
ListItem::new(SharedString::from(format!("debug-scenario-selection-{ix}")))
|
||||
|
|
|
@ -243,6 +243,9 @@ impl Inventory {
|
|||
pub fn list_debug_scenarios(
|
||||
&self,
|
||||
task_contexts: &TaskContexts,
|
||||
lsp_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
add_current_language_tasks: bool,
|
||||
cx: &mut App,
|
||||
) -> (Vec<DebugScenario>, Vec<(TaskSourceKind, DebugScenario)>) {
|
||||
let mut scenarios = Vec::new();
|
||||
|
@ -258,7 +261,6 @@ impl Inventory {
|
|||
}
|
||||
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();
|
||||
|
@ -271,7 +273,14 @@ impl Inventory {
|
|||
language.and_then(|l| l.config().debuggers.first().map(SharedString::from))
|
||||
});
|
||||
if let Some(adapter) = adapter {
|
||||
for (kind, task) in new {
|
||||
for (kind, task) in
|
||||
lsp_tasks
|
||||
.into_iter()
|
||||
.chain(current_resolved_tasks.into_iter().filter(|(kind, _)| {
|
||||
add_current_language_tasks
|
||||
|| !matches!(kind, TaskSourceKind::Language { .. })
|
||||
}))
|
||||
{
|
||||
if let Some(scenario) =
|
||||
DapRegistry::global(cx)
|
||||
.locators()
|
||||
|
|
|
@ -162,15 +162,33 @@ impl TasksModal {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn task_contexts_loaded(
|
||||
pub fn tasks_loaded(
|
||||
&mut self,
|
||||
task_contexts: Arc<TaskContexts>,
|
||||
lsp_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
used_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>,
|
||||
add_current_language_tasks: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let last_used_candidate_index = if used_tasks.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(used_tasks.len() - 1)
|
||||
};
|
||||
let mut new_candidates = used_tasks;
|
||||
new_candidates.extend(lsp_tasks);
|
||||
// todo(debugger): We're always adding lsp tasks here even if prefer_lsp is false
|
||||
// We should move the filter to new_candidates instead of on current
|
||||
// and add a test for this
|
||||
new_candidates.extend(current_resolved_tasks.into_iter().filter(|(task_kind, _)| {
|
||||
add_current_language_tasks || !matches!(task_kind, TaskSourceKind::Language { .. })
|
||||
}));
|
||||
self.picker.update(cx, |picker, cx| {
|
||||
picker.delegate.task_contexts = task_contexts;
|
||||
picker.delegate.candidates = None;
|
||||
picker.delegate.last_used_candidate_index = last_used_candidate_index;
|
||||
picker.delegate.candidates = Some(new_candidates);
|
||||
picker.refresh(window, cx);
|
||||
cx.notify();
|
||||
})
|
||||
|
@ -296,6 +314,9 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
.map(move |(_, task)| (kind.clone(), task))
|
||||
},
|
||||
));
|
||||
// todo(debugger): We're always adding lsp tasks here even if prefer_lsp is false
|
||||
// We should move the filter to new_candidates instead of on current
|
||||
// and add a test for this
|
||||
new_candidates.extend(current.into_iter().filter(
|
||||
|(task_kind, _)| {
|
||||
add_current_language_tasks
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue