Unify the tasks modal and the new session modal (#31646)
Release Notes: - Debugger Beta: added a button to the quick action bar to start a debug session or spawn a task, depending on which of these actions was taken most recently. - Debugger Beta: incorporated the tasks modal into the new session modal as an additional tab. --------- Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com> Co-authored-by: Julia Ryan <p1n3appl3@users.noreply.github.com> Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
804de3316e
commit
1445af559b
12 changed files with 434 additions and 224 deletions
|
@ -1019,5 +1019,12 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"enter": "menu::Confirm"
|
"enter": "menu::Confirm"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "RunModal",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-tab": "pane::ActivateNextItem",
|
||||||
|
"ctrl-shift-tab": "pane::ActivatePreviousItem"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1109,5 +1109,13 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"enter": "menu::Confirm"
|
"enter": "menu::Confirm"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "RunModal",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-tab": "pane::ActivateNextItem",
|
||||||
|
"ctrl-shift-tab": "pane::ActivatePreviousItem"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
|
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
|
||||||
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart,
|
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart,
|
||||||
ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints,
|
ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints,
|
||||||
ToggleSessionPicker, ToggleThreadPicker, persistence,
|
ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
|
@ -65,6 +65,7 @@ pub struct DebugPanel {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||||
|
debug_scenario_scheduled_last: bool,
|
||||||
pub(crate) thread_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
|
pub(crate) thread_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
pub(crate) session_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
|
pub(crate) session_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
|
@ -103,6 +104,7 @@ impl DebugPanel {
|
||||||
thread_picker_menu_handle,
|
thread_picker_menu_handle,
|
||||||
session_picker_menu_handle,
|
session_picker_menu_handle,
|
||||||
_subscriptions: [focus_subscription],
|
_subscriptions: [focus_subscription],
|
||||||
|
debug_scenario_scheduled_last: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -264,6 +266,7 @@ impl DebugPanel {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
self.debug_scenario_scheduled_last = true;
|
||||||
if let Some(inventory) = self
|
if let Some(inventory) = self
|
||||||
.project
|
.project
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -1381,4 +1384,30 @@ impl workspace::DebuggerProvider for DebuggerProvider {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_task_or_modal(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
action: &tasks_ui::Spawn,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Workspace>,
|
||||||
|
) {
|
||||||
|
spawn_task_or_modal(workspace, action, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_scenario_scheduled(&self, cx: &mut App) {
|
||||||
|
self.0.update(cx, |this, _| {
|
||||||
|
this.debug_scenario_scheduled_last = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_scheduled(&self, cx: &mut App) {
|
||||||
|
self.0.update(cx, |this, _| {
|
||||||
|
this.debug_scenario_scheduled_last = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_scenario_scheduled_last(&self, cx: &App) -> bool {
|
||||||
|
self.0.read(cx).debug_scenario_scheduled_last
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use debugger_panel::{DebugPanel, ToggleFocus};
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt};
|
use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt};
|
||||||
use gpui::{App, EntityInputHandler, actions};
|
use gpui::{App, EntityInputHandler, actions};
|
||||||
use new_session_modal::NewSessionModal;
|
use new_session_modal::{NewSessionModal, NewSessionMode};
|
||||||
use project::debugger::{self, breakpoint_store::SourceBreakpoint};
|
use project::debugger::{self, breakpoint_store::SourceBreakpoint};
|
||||||
use session::DebugSession;
|
use session::DebugSession;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use stack_trace_view::StackTraceView;
|
use stack_trace_view::StackTraceView;
|
||||||
|
use tasks_ui::{Spawn, TaskOverrides};
|
||||||
use util::maybe;
|
use util::maybe;
|
||||||
use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
|
use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ pub fn init(cx: &mut App) {
|
||||||
|
|
||||||
cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |workspace, _, _| {
|
cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |workspace, _, _| {
|
||||||
workspace
|
workspace
|
||||||
|
.register_action(spawn_task_or_modal)
|
||||||
.register_action(|workspace, _: &ToggleFocus, window, cx| {
|
.register_action(|workspace, _: &ToggleFocus, window, cx| {
|
||||||
workspace.toggle_panel_focus::<DebugPanel>(window, cx);
|
workspace.toggle_panel_focus::<DebugPanel>(window, cx);
|
||||||
})
|
})
|
||||||
|
@ -208,7 +210,7 @@ pub fn init(cx: &mut App) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
|
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
|
||||||
NewSessionModal::show(workspace, window, cx);
|
NewSessionModal::show(workspace, window, NewSessionMode::Launch, None, cx);
|
||||||
})
|
})
|
||||||
.register_action(
|
.register_action(
|
||||||
|workspace: &mut Workspace, _: &RerunLastSession, window, cx| {
|
|workspace: &mut Workspace, _: &RerunLastSession, window, cx| {
|
||||||
|
@ -309,3 +311,48 @@ pub fn init(cx: &mut App) {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_task_or_modal(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
action: &Spawn,
|
||||||
|
window: &mut ui::Window,
|
||||||
|
cx: &mut ui::Context<Workspace>,
|
||||||
|
) {
|
||||||
|
match action {
|
||||||
|
Spawn::ByName {
|
||||||
|
task_name,
|
||||||
|
reveal_target,
|
||||||
|
} => {
|
||||||
|
let overrides = reveal_target.map(|reveal_target| TaskOverrides {
|
||||||
|
reveal_target: Some(reveal_target),
|
||||||
|
});
|
||||||
|
let name = task_name.clone();
|
||||||
|
tasks_ui::spawn_tasks_filtered(
|
||||||
|
move |(_, task)| task.label.eq(&name),
|
||||||
|
overrides,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.detach_and_log_err(cx)
|
||||||
|
}
|
||||||
|
Spawn::ByTag {
|
||||||
|
task_tag,
|
||||||
|
reveal_target,
|
||||||
|
} => {
|
||||||
|
let overrides = reveal_target.map(|reveal_target| TaskOverrides {
|
||||||
|
reveal_target: Some(reveal_target),
|
||||||
|
});
|
||||||
|
let tag = task_tag.clone();
|
||||||
|
tasks_ui::spawn_tasks_filtered(
|
||||||
|
move |(_, task)| task.tags.contains(&tag),
|
||||||
|
overrides,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.detach_and_log_err(cx)
|
||||||
|
}
|
||||||
|
Spawn::ViaModal { reveal_target } => {
|
||||||
|
NewSessionModal::show(workspace, window, NewSessionMode::Task, *reveal_target, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
usize,
|
usize,
|
||||||
};
|
};
|
||||||
|
use tasks_ui::{TaskOverrides, TasksModal};
|
||||||
|
|
||||||
use dap::{
|
use dap::{
|
||||||
DapRegistry, DebugRequest, TelemetrySpawnLocation, adapters::DebugAdapterName, send_telemetry,
|
DapRegistry, DebugRequest, TelemetrySpawnLocation, adapters::DebugAdapterName, send_telemetry,
|
||||||
|
@ -16,12 +17,12 @@ use editor::{Anchor, Editor, EditorElement, EditorStyle, scroll::Autoscroll};
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Animation, AnimationExt as _, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle,
|
Animation, AnimationExt as _, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle,
|
||||||
Focusable, Render, Subscription, TextStyle, Transformation, WeakEntity, percentage,
|
Focusable, KeyContext, Render, Subscription, TextStyle, Transformation, WeakEntity, percentage,
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
|
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
|
||||||
use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore};
|
use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use task::{DebugScenario, LaunchRequest, ZedDebugConfig};
|
use task::{DebugScenario, LaunchRequest, RevealTarget, ZedDebugConfig};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
|
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
|
||||||
|
@ -47,10 +48,11 @@ pub(super) struct NewSessionModal {
|
||||||
mode: NewSessionMode,
|
mode: NewSessionMode,
|
||||||
launch_picker: Entity<Picker<DebugScenarioDelegate>>,
|
launch_picker: Entity<Picker<DebugScenarioDelegate>>,
|
||||||
attach_mode: Entity<AttachMode>,
|
attach_mode: Entity<AttachMode>,
|
||||||
custom_mode: Entity<CustomMode>,
|
configure_mode: Entity<ConfigureMode>,
|
||||||
|
task_mode: TaskMode,
|
||||||
debugger: Option<DebugAdapterName>,
|
debugger: Option<DebugAdapterName>,
|
||||||
save_scenario_state: Option<SaveScenarioState>,
|
save_scenario_state: Option<SaveScenarioState>,
|
||||||
_subscriptions: [Subscription; 2],
|
_subscriptions: [Subscription; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString {
|
fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString {
|
||||||
|
@ -75,6 +77,8 @@ impl NewSessionModal {
|
||||||
pub(super) fn show(
|
pub(super) fn show(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
mode: NewSessionMode,
|
||||||
|
reveal_target: Option<RevealTarget>,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) {
|
) {
|
||||||
let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
|
let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
|
||||||
|
@ -84,20 +88,50 @@ impl NewSessionModal {
|
||||||
let languages = workspace.app_state().languages.clone();
|
let languages = workspace.app_state().languages.clone();
|
||||||
|
|
||||||
cx.spawn_in(window, async move |workspace, cx| {
|
cx.spawn_in(window, async move |workspace, cx| {
|
||||||
|
let task_contexts = workspace
|
||||||
|
.update_in(cx, |workspace, window, cx| {
|
||||||
|
tasks_ui::task_contexts(workspace, window, cx)
|
||||||
|
})?
|
||||||
|
.await;
|
||||||
|
let task_contexts = Arc::new(task_contexts);
|
||||||
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| {
|
||||||
let attach_mode = AttachMode::new(None, workspace_handle.clone(), window, cx);
|
let attach_mode = AttachMode::new(None, workspace_handle.clone(), window, cx);
|
||||||
|
|
||||||
let launch_picker = cx.new(|cx| {
|
let launch_picker = cx.new(|cx| {
|
||||||
Picker::uniform_list(
|
let mut delegate =
|
||||||
DebugScenarioDelegate::new(debug_panel.downgrade(), task_store),
|
DebugScenarioDelegate::new(debug_panel.downgrade(), task_store.clone());
|
||||||
window,
|
delegate.task_contexts_loaded(task_contexts.clone(), languages, window, cx);
|
||||||
cx,
|
Picker::uniform_list(delegate, window, cx).modal(false)
|
||||||
)
|
|
||||||
.modal(false)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let configure_mode = ConfigureMode::new(None, window, cx);
|
||||||
|
if let Some(active_cwd) = task_contexts
|
||||||
|
.active_context()
|
||||||
|
.and_then(|context| context.cwd.clone())
|
||||||
|
{
|
||||||
|
configure_mode.update(cx, |configure_mode, cx| {
|
||||||
|
configure_mode.load(active_cwd, window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let task_overrides = Some(TaskOverrides { reveal_target });
|
||||||
|
|
||||||
|
let task_mode = TaskMode {
|
||||||
|
task_modal: cx.new(|cx| {
|
||||||
|
TasksModal::new(
|
||||||
|
task_store.clone(),
|
||||||
|
task_contexts,
|
||||||
|
task_overrides,
|
||||||
|
false,
|
||||||
|
workspace_handle.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
let _subscriptions = [
|
let _subscriptions = [
|
||||||
cx.subscribe(&launch_picker, |_, _, _, cx| {
|
cx.subscribe(&launch_picker, |_, _, _, cx| {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
|
@ -108,52 +142,18 @@ impl NewSessionModal {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
cx.subscribe(&task_mode.task_modal, |_, _, _: &DismissEvent, cx| {
|
||||||
|
cx.emit(DismissEvent)
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let custom_mode = CustomMode::new(None, 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.debugger = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.launch_picker.update(cx, |picker, cx| {
|
|
||||||
picker.delegate.task_contexts_loaded(
|
|
||||||
task_contexts,
|
|
||||||
languages,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
picker.refresh(window, cx);
|
|
||||||
cx.notify();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
launch_picker,
|
launch_picker,
|
||||||
attach_mode,
|
attach_mode,
|
||||||
custom_mode,
|
configure_mode,
|
||||||
|
task_mode,
|
||||||
debugger: None,
|
debugger: None,
|
||||||
mode: NewSessionMode::Launch,
|
mode,
|
||||||
debug_panel: debug_panel.downgrade(),
|
debug_panel: debug_panel.downgrade(),
|
||||||
workspace: workspace_handle,
|
workspace: workspace_handle,
|
||||||
save_scenario_state: None,
|
save_scenario_state: None,
|
||||||
|
@ -170,10 +170,17 @@ impl NewSessionModal {
|
||||||
fn render_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
fn render_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||||
let dap_menu = self.adapter_drop_down_menu(window, cx);
|
let dap_menu = self.adapter_drop_down_menu(window, cx);
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
NewSessionMode::Task => self
|
||||||
|
.task_mode
|
||||||
|
.task_modal
|
||||||
|
.read(cx)
|
||||||
|
.picker
|
||||||
|
.clone()
|
||||||
|
.into_any_element(),
|
||||||
NewSessionMode::Attach => self.attach_mode.update(cx, |this, cx| {
|
NewSessionMode::Attach => self.attach_mode.update(cx, |this, cx| {
|
||||||
this.clone().render(window, cx).into_any_element()
|
this.clone().render(window, cx).into_any_element()
|
||||||
}),
|
}),
|
||||||
NewSessionMode::Custom => self.custom_mode.update(cx, |this, cx| {
|
NewSessionMode::Configure => self.configure_mode.update(cx, |this, cx| {
|
||||||
this.clone().render(dap_menu, window, cx).into_any_element()
|
this.clone().render(dap_menu, window, cx).into_any_element()
|
||||||
}),
|
}),
|
||||||
NewSessionMode::Launch => v_flex()
|
NewSessionMode::Launch => v_flex()
|
||||||
|
@ -185,16 +192,17 @@ impl NewSessionModal {
|
||||||
|
|
||||||
fn mode_focus_handle(&self, cx: &App) -> FocusHandle {
|
fn mode_focus_handle(&self, cx: &App) -> FocusHandle {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
NewSessionMode::Task => self.task_mode.task_modal.focus_handle(cx),
|
||||||
NewSessionMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx),
|
NewSessionMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx),
|
||||||
NewSessionMode::Custom => self.custom_mode.read(cx).program.focus_handle(cx),
|
NewSessionMode::Configure => self.configure_mode.read(cx).program.focus_handle(cx),
|
||||||
NewSessionMode::Launch => self.launch_picker.focus_handle(cx),
|
NewSessionMode::Launch => self.launch_picker.focus_handle(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_scenario(&self, debugger: &str, cx: &App) -> Option<DebugScenario> {
|
fn debug_scenario(&self, debugger: &str, cx: &App) -> Option<DebugScenario> {
|
||||||
let request = match self.mode {
|
let request = match self.mode {
|
||||||
NewSessionMode::Custom => Some(DebugRequest::Launch(
|
NewSessionMode::Configure => Some(DebugRequest::Launch(
|
||||||
self.custom_mode.read(cx).debug_request(cx),
|
self.configure_mode.read(cx).debug_request(cx),
|
||||||
)),
|
)),
|
||||||
NewSessionMode::Attach => Some(DebugRequest::Attach(
|
NewSessionMode::Attach => Some(DebugRequest::Attach(
|
||||||
self.attach_mode.read(cx).debug_request(),
|
self.attach_mode.read(cx).debug_request(),
|
||||||
|
@ -203,8 +211,8 @@ impl NewSessionModal {
|
||||||
}?;
|
}?;
|
||||||
let label = suggested_label(&request, debugger);
|
let label = suggested_label(&request, debugger);
|
||||||
|
|
||||||
let stop_on_entry = if let NewSessionMode::Custom = &self.mode {
|
let stop_on_entry = if let NewSessionMode::Configure = &self.mode {
|
||||||
Some(self.custom_mode.read(cx).stop_on_entry.selected())
|
Some(self.configure_mode.read(cx).stop_on_entry.selected())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -527,7 +535,8 @@ static SELECT_DEBUGGER_LABEL: SharedString = SharedString::new_static("Select De
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) enum NewSessionMode {
|
pub(crate) enum NewSessionMode {
|
||||||
Custom,
|
Task,
|
||||||
|
Configure,
|
||||||
Attach,
|
Attach,
|
||||||
Launch,
|
Launch,
|
||||||
}
|
}
|
||||||
|
@ -535,9 +544,10 @@ pub(crate) enum NewSessionMode {
|
||||||
impl std::fmt::Display for NewSessionMode {
|
impl std::fmt::Display for NewSessionMode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mode = match self {
|
let mode = match self {
|
||||||
NewSessionMode::Launch => "Launch".to_owned(),
|
NewSessionMode::Task => "Run",
|
||||||
NewSessionMode::Attach => "Attach".to_owned(),
|
NewSessionMode::Launch => "Debug",
|
||||||
NewSessionMode::Custom => "Custom".to_owned(),
|
NewSessionMode::Attach => "Attach",
|
||||||
|
NewSessionMode::Configure => "Configure Debugger",
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "{}", mode)
|
write!(f, "{}", mode)
|
||||||
|
@ -597,36 +607,39 @@ impl Render for NewSessionModal {
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.w(rems(34.))
|
.w(rems(34.))
|
||||||
.key_context("Pane")
|
.key_context({
|
||||||
|
let mut key_context = KeyContext::new_with_defaults();
|
||||||
|
key_context.add("Pane");
|
||||||
|
key_context.add("RunModal");
|
||||||
|
key_context
|
||||||
|
})
|
||||||
.elevation_3(cx)
|
.elevation_3(cx)
|
||||||
.bg(cx.theme().colors().elevated_surface_background)
|
.bg(cx.theme().colors().elevated_surface_background)
|
||||||
.on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
|
.on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
}))
|
}))
|
||||||
|
.on_action(cx.listener(|this, _: &pane::ActivateNextItem, window, cx| {
|
||||||
|
this.mode = match this.mode {
|
||||||
|
NewSessionMode::Task => NewSessionMode::Launch,
|
||||||
|
NewSessionMode::Launch => NewSessionMode::Attach,
|
||||||
|
NewSessionMode::Attach => NewSessionMode::Configure,
|
||||||
|
NewSessionMode::Configure => NewSessionMode::Task,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.mode_focus_handle(cx).focus(window);
|
||||||
|
}))
|
||||||
.on_action(
|
.on_action(
|
||||||
cx.listener(|this, _: &pane::ActivatePreviousItem, window, cx| {
|
cx.listener(|this, _: &pane::ActivatePreviousItem, window, cx| {
|
||||||
this.mode = match this.mode {
|
this.mode = match this.mode {
|
||||||
|
NewSessionMode::Task => NewSessionMode::Configure,
|
||||||
|
NewSessionMode::Launch => NewSessionMode::Task,
|
||||||
NewSessionMode::Attach => NewSessionMode::Launch,
|
NewSessionMode::Attach => NewSessionMode::Launch,
|
||||||
NewSessionMode::Launch => NewSessionMode::Attach,
|
NewSessionMode::Configure => NewSessionMode::Attach,
|
||||||
_ => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mode_focus_handle(cx).focus(window);
|
this.mode_focus_handle(cx).focus(window);
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.on_action(cx.listener(|this, _: &pane::ActivateNextItem, window, cx| {
|
|
||||||
this.mode = match this.mode {
|
|
||||||
NewSessionMode::Attach => NewSessionMode::Launch,
|
|
||||||
NewSessionMode::Launch => NewSessionMode::Attach,
|
|
||||||
_ => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.mode_focus_handle(cx).focus(window);
|
|
||||||
}))
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
@ -637,37 +650,73 @@ impl Render for NewSessionModal {
|
||||||
.justify_start()
|
.justify_start()
|
||||||
.w_full()
|
.w_full()
|
||||||
.child(
|
.child(
|
||||||
ToggleButton::new("debugger-session-ui-picker-button", "Launch")
|
ToggleButton::new(
|
||||||
.size(ButtonSize::Default)
|
"debugger-session-ui-tasks-button",
|
||||||
.style(ui::ButtonStyle::Subtle)
|
NewSessionMode::Task.to_string(),
|
||||||
.toggle_state(matches!(self.mode, NewSessionMode::Launch))
|
)
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
.size(ButtonSize::Default)
|
||||||
this.mode = NewSessionMode::Launch;
|
.toggle_state(matches!(self.mode, NewSessionMode::Task))
|
||||||
this.mode_focus_handle(cx).focus(window);
|
.style(ui::ButtonStyle::Subtle)
|
||||||
cx.notify();
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
}))
|
this.mode = NewSessionMode::Task;
|
||||||
.first(),
|
this.mode_focus_handle(cx).focus(window);
|
||||||
|
cx.notify();
|
||||||
|
}))
|
||||||
|
.first(),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
ToggleButton::new("debugger-session-ui-attach-button", "Attach")
|
ToggleButton::new(
|
||||||
.size(ButtonSize::Default)
|
"debugger-session-ui-launch-button",
|
||||||
.toggle_state(matches!(self.mode, NewSessionMode::Attach))
|
NewSessionMode::Launch.to_string(),
|
||||||
.style(ui::ButtonStyle::Subtle)
|
)
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
.size(ButtonSize::Default)
|
||||||
this.mode = NewSessionMode::Attach;
|
.style(ui::ButtonStyle::Subtle)
|
||||||
|
.toggle_state(matches!(self.mode, NewSessionMode::Launch))
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.mode = NewSessionMode::Launch;
|
||||||
|
this.mode_focus_handle(cx).focus(window);
|
||||||
|
cx.notify();
|
||||||
|
}))
|
||||||
|
.middle(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new(
|
||||||
|
"debugger-session-ui-attach-button",
|
||||||
|
NewSessionMode::Attach.to_string(),
|
||||||
|
)
|
||||||
|
.size(ButtonSize::Default)
|
||||||
|
.toggle_state(matches!(self.mode, NewSessionMode::Attach))
|
||||||
|
.style(ui::ButtonStyle::Subtle)
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.mode = NewSessionMode::Attach;
|
||||||
|
|
||||||
if let Some(debugger) = this.debugger.as_ref() {
|
if let Some(debugger) = this.debugger.as_ref() {
|
||||||
Self::update_attach_picker(
|
Self::update_attach_picker(
|
||||||
&this.attach_mode,
|
&this.attach_mode,
|
||||||
&debugger,
|
&debugger,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.mode_focus_handle(cx).focus(window);
|
this.mode_focus_handle(cx).focus(window);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}))
|
}))
|
||||||
.last(),
|
.middle(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new(
|
||||||
|
"debugger-session-ui-custom-button",
|
||||||
|
NewSessionMode::Configure.to_string(),
|
||||||
|
)
|
||||||
|
.size(ButtonSize::Default)
|
||||||
|
.toggle_state(matches!(self.mode, NewSessionMode::Configure))
|
||||||
|
.style(ui::ButtonStyle::Subtle)
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.mode = NewSessionMode::Configure;
|
||||||
|
this.mode_focus_handle(cx).focus(window);
|
||||||
|
cx.notify();
|
||||||
|
}))
|
||||||
|
.last(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.justify_between()
|
.justify_between()
|
||||||
|
@ -675,83 +724,83 @@ impl Render for NewSessionModal {
|
||||||
.border_b_1(),
|
.border_b_1(),
|
||||||
)
|
)
|
||||||
.child(v_flex().child(self.render_mode(window, cx)))
|
.child(v_flex().child(self.render_mode(window, cx)))
|
||||||
.child(
|
.map(|el| {
|
||||||
h_flex()
|
let container = h_flex()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.p_2()
|
.p_2()
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.w_full()
|
.w_full();
|
||||||
.child(match self.mode {
|
match self.mode {
|
||||||
NewSessionMode::Attach => {
|
NewSessionMode::Configure => el.child(
|
||||||
div().child(self.adapter_drop_down_menu(window, cx))
|
container
|
||||||
}
|
|
||||||
NewSessionMode::Launch => div().child(
|
|
||||||
Button::new("new-session-modal-custom", "Custom").on_click({
|
|
||||||
let this = cx.weak_entity();
|
|
||||||
move |_, window, cx| {
|
|
||||||
this.update(cx, |this, cx| {
|
|
||||||
this.mode = NewSessionMode::Custom;
|
|
||||||
this.mode_focus_handle(cx).focus(window);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
NewSessionMode::Custom => h_flex()
|
|
||||||
.child(
|
.child(
|
||||||
Button::new("new-session-modal-back", "Save to .zed/debug.json...")
|
h_flex()
|
||||||
|
.child(
|
||||||
|
Button::new(
|
||||||
|
"new-session-modal-back",
|
||||||
|
"Save to .zed/debug.json...",
|
||||||
|
)
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.save_debug_scenario(window, cx);
|
||||||
|
}))
|
||||||
|
.disabled(
|
||||||
|
self.debugger.is_none()
|
||||||
|
|| self
|
||||||
|
.configure_mode
|
||||||
|
.read(cx)
|
||||||
|
.program
|
||||||
|
.read(cx)
|
||||||
|
.is_empty(cx)
|
||||||
|
|| self.save_scenario_state.is_some(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(self.render_save_state(cx)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("debugger-spawn", "Start")
|
||||||
.on_click(cx.listener(|this, _, window, cx| {
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
this.save_debug_scenario(window, cx);
|
this.start_new_session(window, cx)
|
||||||
}))
|
}))
|
||||||
.disabled(
|
.disabled(
|
||||||
self.debugger.is_none()
|
self.debugger.is_none()
|
||||||
|| self
|
|| self
|
||||||
.custom_mode
|
.configure_mode
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.program
|
.program
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.is_empty(cx)
|
.is_empty(cx),
|
||||||
|| self.save_scenario_state.is_some(),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
.child(self.render_save_state(cx)),
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
Button::new("debugger-spawn", "Start")
|
|
||||||
.on_click(cx.listener(|this, _, window, cx| match &this.mode {
|
|
||||||
NewSessionMode::Launch => {
|
|
||||||
this.launch_picker.update(cx, |picker, cx| {
|
|
||||||
picker.delegate.confirm(true, window, cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => this.start_new_session(window, cx),
|
|
||||||
}))
|
|
||||||
.disabled(match self.mode {
|
|
||||||
NewSessionMode::Launch => {
|
|
||||||
!self.launch_picker.read(cx).delegate.matches.is_empty()
|
|
||||||
}
|
|
||||||
NewSessionMode::Attach => {
|
|
||||||
self.debugger.is_none()
|
|
||||||
|| self
|
|
||||||
.attach_mode
|
|
||||||
.read(cx)
|
|
||||||
.attach_picker
|
|
||||||
.read(cx)
|
|
||||||
.picker
|
|
||||||
.read(cx)
|
|
||||||
.delegate
|
|
||||||
.match_count()
|
|
||||||
== 0
|
|
||||||
}
|
|
||||||
NewSessionMode::Custom => {
|
|
||||||
self.debugger.is_none()
|
|
||||||
|| self.custom_mode.read(cx).program.read(cx).is_empty(cx)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
)
|
NewSessionMode::Attach => el.child(
|
||||||
|
container
|
||||||
|
.child(div().child(self.adapter_drop_down_menu(window, cx)))
|
||||||
|
.child(
|
||||||
|
Button::new("debugger-spawn", "Start")
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.start_new_session(window, cx)
|
||||||
|
}))
|
||||||
|
.disabled(
|
||||||
|
self.debugger.is_none()
|
||||||
|
|| self
|
||||||
|
.attach_mode
|
||||||
|
.read(cx)
|
||||||
|
.attach_picker
|
||||||
|
.read(cx)
|
||||||
|
.picker
|
||||||
|
.read(cx)
|
||||||
|
.delegate
|
||||||
|
.match_count()
|
||||||
|
== 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
NewSessionMode::Launch => el,
|
||||||
|
NewSessionMode::Task => el,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,13 +823,13 @@ impl RenderOnce for AttachMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct CustomMode {
|
pub(super) struct ConfigureMode {
|
||||||
program: Entity<Editor>,
|
program: Entity<Editor>,
|
||||||
cwd: Entity<Editor>,
|
cwd: Entity<Editor>,
|
||||||
stop_on_entry: ToggleState,
|
stop_on_entry: ToggleState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomMode {
|
impl ConfigureMode {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
past_launch_config: Option<LaunchRequest>,
|
past_launch_config: Option<LaunchRequest>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -940,6 +989,11 @@ impl AttachMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(super) struct TaskMode {
|
||||||
|
pub(super) task_modal: Entity<TasksModal>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct DebugScenarioDelegate {
|
pub(super) struct DebugScenarioDelegate {
|
||||||
task_store: Entity<TaskStore>,
|
task_store: Entity<TaskStore>,
|
||||||
candidates: Vec<(Option<TaskSourceKind>, DebugScenario)>,
|
candidates: Vec<(Option<TaskSourceKind>, DebugScenario)>,
|
||||||
|
@ -995,12 +1049,12 @@ impl DebugScenarioDelegate {
|
||||||
|
|
||||||
pub fn task_contexts_loaded(
|
pub fn task_contexts_loaded(
|
||||||
&mut self,
|
&mut self,
|
||||||
task_contexts: TaskContexts,
|
task_contexts: Arc<TaskContexts>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Picker<Self>>,
|
cx: &mut Context<Picker<Self>>,
|
||||||
) {
|
) {
|
||||||
self.task_contexts = Some(Arc::new(task_contexts));
|
self.task_contexts = Some(task_contexts);
|
||||||
|
|
||||||
let (recent, scenarios) = self
|
let (recent, scenarios) = self
|
||||||
.task_store
|
.task_store
|
||||||
|
@ -1206,7 +1260,7 @@ pub(crate) fn resolve_path(path: &mut String) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl NewSessionModal {
|
impl NewSessionModal {
|
||||||
pub(crate) fn set_custom(
|
pub(crate) fn set_configure(
|
||||||
&mut self,
|
&mut self,
|
||||||
program: impl AsRef<str>,
|
program: impl AsRef<str>,
|
||||||
cwd: impl AsRef<str>,
|
cwd: impl AsRef<str>,
|
||||||
|
@ -1214,21 +1268,21 @@ impl NewSessionModal {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
self.mode = NewSessionMode::Custom;
|
self.mode = NewSessionMode::Configure;
|
||||||
self.debugger = Some(dap::adapters::DebugAdapterName("fake-adapter".into()));
|
self.debugger = Some(dap::adapters::DebugAdapterName("fake-adapter".into()));
|
||||||
|
|
||||||
self.custom_mode.update(cx, |custom, cx| {
|
self.configure_mode.update(cx, |configure, cx| {
|
||||||
custom.program.update(cx, |editor, cx| {
|
configure.program.update(cx, |editor, cx| {
|
||||||
editor.clear(window, cx);
|
editor.clear(window, cx);
|
||||||
editor.set_text(program.as_ref(), window, cx);
|
editor.set_text(program.as_ref(), window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
custom.cwd.update(cx, |editor, cx| {
|
configure.cwd.update(cx, |editor, cx| {
|
||||||
editor.clear(window, cx);
|
editor.clear(window, cx);
|
||||||
editor.set_text(cwd.as_ref(), window, cx);
|
editor.set_text(cwd.as_ref(), window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
custom.stop_on_entry = match stop_on_entry {
|
configure.stop_on_entry = match stop_on_entry {
|
||||||
true => ToggleState::Selected,
|
true => ToggleState::Selected,
|
||||||
_ => ToggleState::Unselected,
|
_ => ToggleState::Unselected,
|
||||||
}
|
}
|
||||||
|
@ -1239,28 +1293,3 @@ impl NewSessionModal {
|
||||||
self.save_debug_scenario(window, cx);
|
self.save_debug_scenario(window, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use paths::home_dir;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_normalize_paths() {
|
|
||||||
let sep = std::path::MAIN_SEPARATOR;
|
|
||||||
let home = home_dir().to_string_lossy().to_string();
|
|
||||||
let resolve_path = |path: &str| -> String {
|
|
||||||
let mut path = path.to_string();
|
|
||||||
super::resolve_path(&mut path);
|
|
||||||
path
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(resolve_path("bin"), format!("bin"));
|
|
||||||
assert_eq!(resolve_path(&format!("{sep}foo")), format!("{sep}foo"));
|
|
||||||
assert_eq!(resolve_path(""), format!(""));
|
|
||||||
assert_eq!(
|
|
||||||
resolve_path(&format!("~{sep}blah")),
|
|
||||||
format!("{home}{sep}blah")
|
|
||||||
);
|
|
||||||
assert_eq!(resolve_path("~"), home);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use task::{DebugRequest, DebugScenario, LaunchRequest, TaskContext, VariableName, ZedDebugConfig};
|
use task::{DebugRequest, DebugScenario, LaunchRequest, TaskContext, VariableName, ZedDebugConfig};
|
||||||
use util::path;
|
use util::path;
|
||||||
|
|
||||||
|
use crate::new_session_modal::NewSessionMode;
|
||||||
use crate::tests::{init_test, init_test_workspace};
|
use crate::tests::{init_test, init_test_workspace};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -170,7 +171,13 @@ async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut
|
||||||
|
|
||||||
workspace
|
workspace
|
||||||
.update(cx, |workspace, window, cx| {
|
.update(cx, |workspace, window, cx| {
|
||||||
crate::new_session_modal::NewSessionModal::show(workspace, window, cx);
|
crate::new_session_modal::NewSessionModal::show(
|
||||||
|
workspace,
|
||||||
|
window,
|
||||||
|
NewSessionMode::Launch,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -184,7 +191,7 @@ async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut
|
||||||
.expect("Modal should be active");
|
.expect("Modal should be active");
|
||||||
|
|
||||||
modal.update_in(cx, |modal, window, cx| {
|
modal.update_in(cx, |modal, window, cx| {
|
||||||
modal.set_custom("/project/main", "/project", false, window, cx);
|
modal.set_configure("/project/main", "/project", false, window, cx);
|
||||||
modal.save_scenario(window, cx);
|
modal.save_scenario(window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -213,7 +220,7 @@ async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut
|
||||||
pretty_assertions::assert_eq!(expected_content, actual_lines);
|
pretty_assertions::assert_eq!(expected_content, actual_lines);
|
||||||
|
|
||||||
modal.update_in(cx, |modal, window, cx| {
|
modal.update_in(cx, |modal, window, cx| {
|
||||||
modal.set_custom("/project/other", "/project", true, window, cx);
|
modal.set_configure("/project/other", "/project", true, window, cx);
|
||||||
modal.save_scenario(window, cx);
|
modal.save_scenario(window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
///
|
///
|
||||||
/// The keybindings themselves are managed independently by calling cx.bind_keys().
|
/// The keybindings themselves are managed independently by calling cx.bind_keys().
|
||||||
/// (Though mostly when developing Zed itself, you just need to add a new line to
|
/// (Though mostly when developing Zed itself, you just need to add a new line to
|
||||||
/// assets/keymaps/default.json).
|
/// assets/keymaps/default-{platform}.json).
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// cx.bind_keys([
|
/// cx.bind_keys([
|
||||||
|
|
|
@ -23,7 +23,7 @@ use workspace::{ModalView, Workspace};
|
||||||
pub use zed_actions::{Rerun, Spawn};
|
pub use zed_actions::{Rerun, Spawn};
|
||||||
|
|
||||||
/// A modal used to spawn new tasks.
|
/// A modal used to spawn new tasks.
|
||||||
pub(crate) struct TasksModalDelegate {
|
pub struct TasksModalDelegate {
|
||||||
task_store: Entity<TaskStore>,
|
task_store: Entity<TaskStore>,
|
||||||
candidates: Option<Vec<(TaskSourceKind, ResolvedTask)>>,
|
candidates: Option<Vec<(TaskSourceKind, ResolvedTask)>>,
|
||||||
task_overrides: Option<TaskOverrides>,
|
task_overrides: Option<TaskOverrides>,
|
||||||
|
@ -33,21 +33,21 @@ pub(crate) struct TasksModalDelegate {
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
prompt: String,
|
prompt: String,
|
||||||
task_contexts: TaskContexts,
|
task_contexts: Arc<TaskContexts>,
|
||||||
placeholder_text: Arc<str>,
|
placeholder_text: Arc<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Task template amendments to do before resolving the context.
|
/// Task template amendments to do before resolving the context.
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub(crate) struct TaskOverrides {
|
pub struct TaskOverrides {
|
||||||
/// See [`RevealTarget`].
|
/// See [`RevealTarget`].
|
||||||
pub(crate) reveal_target: Option<RevealTarget>,
|
pub reveal_target: Option<RevealTarget>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TasksModalDelegate {
|
impl TasksModalDelegate {
|
||||||
fn new(
|
fn new(
|
||||||
task_store: Entity<TaskStore>,
|
task_store: Entity<TaskStore>,
|
||||||
task_contexts: TaskContexts,
|
task_contexts: Arc<TaskContexts>,
|
||||||
task_overrides: Option<TaskOverrides>,
|
task_overrides: Option<TaskOverrides>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -123,15 +123,16 @@ impl TasksModalDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TasksModal {
|
pub struct TasksModal {
|
||||||
picker: Entity<Picker<TasksModalDelegate>>,
|
pub picker: Entity<Picker<TasksModalDelegate>>,
|
||||||
_subscription: [Subscription; 2],
|
_subscription: [Subscription; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TasksModal {
|
impl TasksModal {
|
||||||
pub(crate) fn new(
|
pub fn new(
|
||||||
task_store: Entity<TaskStore>,
|
task_store: Entity<TaskStore>,
|
||||||
task_contexts: TaskContexts,
|
task_contexts: Arc<TaskContexts>,
|
||||||
task_overrides: Option<TaskOverrides>,
|
task_overrides: Option<TaskOverrides>,
|
||||||
|
is_modal: bool,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
|
@ -142,6 +143,7 @@ impl TasksModal {
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
.modal(is_modal)
|
||||||
});
|
});
|
||||||
let _subscription = [
|
let _subscription = [
|
||||||
cx.subscribe(&picker, |_, _, _: &DismissEvent, cx| {
|
cx.subscribe(&picker, |_, _, _: &DismissEvent, cx| {
|
||||||
|
@ -158,6 +160,20 @@ impl TasksModal {
|
||||||
_subscription,
|
_subscription,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn task_contexts_loaded(
|
||||||
|
&mut self,
|
||||||
|
task_contexts: Arc<TaskContexts>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.picker.update(cx, |picker, cx| {
|
||||||
|
picker.delegate.task_contexts = task_contexts;
|
||||||
|
picker.delegate.candidates = None;
|
||||||
|
picker.refresh(window, cx);
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for TasksModal {
|
impl Render for TasksModal {
|
||||||
|
@ -568,6 +584,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_footer(
|
fn render_footer(
|
||||||
&self,
|
&self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::path::Path;
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{App, AppContext as _, Context, Entity, Task, Window};
|
use gpui::{App, AppContext as _, Context, Entity, Task, Window};
|
||||||
use modal::TaskOverrides;
|
|
||||||
use project::{Location, TaskContexts, TaskSourceKind, Worktree};
|
use project::{Location, TaskContexts, TaskSourceKind, Worktree};
|
||||||
use task::{RevealTarget, TaskContext, TaskId, TaskTemplate, TaskVariables, VariableName};
|
use task::{RevealTarget, TaskContext, TaskId, TaskTemplate, TaskVariables, VariableName};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
mod modal;
|
mod modal;
|
||||||
|
|
||||||
pub use modal::{Rerun, ShowAttachModal, Spawn, TasksModal};
|
pub use modal::{Rerun, ShowAttachModal, Spawn, TaskOverrides, TasksModal};
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
cx.observe_new(
|
cx.observe_new(
|
||||||
|
@ -95,6 +94,11 @@ fn spawn_task_or_modal(
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) {
|
) {
|
||||||
|
if let Some(provider) = workspace.debugger_provider() {
|
||||||
|
provider.spawn_task_or_modal(workspace, action, window, cx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
Spawn::ByName {
|
Spawn::ByName {
|
||||||
task_name,
|
task_name,
|
||||||
|
@ -143,7 +147,7 @@ pub fn toggle_modal(
|
||||||
if can_open_modal {
|
if can_open_modal {
|
||||||
let task_contexts = task_contexts(workspace, window, cx);
|
let task_contexts = task_contexts(workspace, window, cx);
|
||||||
cx.spawn_in(window, async move |workspace, cx| {
|
cx.spawn_in(window, async move |workspace, cx| {
|
||||||
let task_contexts = task_contexts.await;
|
let task_contexts = Arc::new(task_contexts.await);
|
||||||
workspace
|
workspace
|
||||||
.update_in(cx, |workspace, window, cx| {
|
.update_in(cx, |workspace, window, cx| {
|
||||||
workspace.toggle_modal(window, cx, |window, cx| {
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
|
@ -153,6 +157,7 @@ pub fn toggle_modal(
|
||||||
reveal_target.map(|target| TaskOverrides {
|
reveal_target.map(|target| TaskOverrides {
|
||||||
reveal_target: Some(target),
|
reveal_target: Some(target),
|
||||||
}),
|
}),
|
||||||
|
true,
|
||||||
workspace_handle,
|
workspace_handle,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -166,7 +171,7 @@ pub fn toggle_modal(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_tasks_filtered<F>(
|
pub fn spawn_tasks_filtered<F>(
|
||||||
mut predicate: F,
|
mut predicate: F,
|
||||||
overrides: Option<TaskOverrides>,
|
overrides: Option<TaskOverrides>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
|
|
@ -56,6 +56,10 @@ impl Workspace {
|
||||||
) {
|
) {
|
||||||
let spawn_in_terminal = resolved_task.resolved.clone();
|
let spawn_in_terminal = resolved_task.resolved.clone();
|
||||||
if !omit_history {
|
if !omit_history {
|
||||||
|
if let Some(debugger_provider) = self.debugger_provider.as_ref() {
|
||||||
|
debugger_provider.task_scheduled(cx);
|
||||||
|
}
|
||||||
|
|
||||||
self.project().update(cx, |project, cx| {
|
self.project().update(cx, |project, cx| {
|
||||||
if let Some(task_inventory) =
|
if let Some(task_inventory) =
|
||||||
project.task_store().read(cx).task_inventory().cloned()
|
project.task_store().read(cx).task_inventory().cloned()
|
||||||
|
|
|
@ -100,13 +100,13 @@ use task::{DebugScenario, SpawnInTerminal, TaskContext};
|
||||||
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
||||||
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
||||||
pub use ui;
|
pub use ui;
|
||||||
use ui::prelude::*;
|
use ui::{Window, prelude::*};
|
||||||
use util::{ResultExt, TryFutureExt, paths::SanitizedPath, serde::default_true};
|
use util::{ResultExt, TryFutureExt, paths::SanitizedPath, serde::default_true};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
pub use workspace_settings::{
|
pub use workspace_settings::{
|
||||||
AutosaveSetting, BottomDockLayout, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,
|
AutosaveSetting, BottomDockLayout, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,
|
||||||
};
|
};
|
||||||
use zed_actions::feedback::FileBugReport;
|
use zed_actions::{Spawn, feedback::FileBugReport};
|
||||||
|
|
||||||
use crate::notifications::NotificationId;
|
use crate::notifications::NotificationId;
|
||||||
use crate::persistence::{
|
use crate::persistence::{
|
||||||
|
@ -149,6 +149,18 @@ pub trait DebuggerProvider {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn spawn_task_or_modal(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
action: &Spawn,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Workspace>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn task_scheduled(&self, cx: &mut App);
|
||||||
|
fn debug_scenario_scheduled(&self, cx: &mut App);
|
||||||
|
fn debug_scenario_scheduled_last(&self, cx: &App) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
|
@ -947,7 +959,7 @@ pub struct Workspace {
|
||||||
on_prompt_for_new_path: Option<PromptForNewPath>,
|
on_prompt_for_new_path: Option<PromptForNewPath>,
|
||||||
on_prompt_for_open_path: Option<PromptForOpenPath>,
|
on_prompt_for_open_path: Option<PromptForOpenPath>,
|
||||||
terminal_provider: Option<Box<dyn TerminalProvider>>,
|
terminal_provider: Option<Box<dyn TerminalProvider>>,
|
||||||
debugger_provider: Option<Box<dyn DebuggerProvider>>,
|
debugger_provider: Option<Arc<dyn DebuggerProvider>>,
|
||||||
serializable_items_tx: UnboundedSender<Box<dyn SerializableItemHandle>>,
|
serializable_items_tx: UnboundedSender<Box<dyn SerializableItemHandle>>,
|
||||||
serialized_ssh_project: Option<SerializedSshProject>,
|
serialized_ssh_project: Option<SerializedSshProject>,
|
||||||
_items_serializer: Task<Result<()>>,
|
_items_serializer: Task<Result<()>>,
|
||||||
|
@ -1828,7 +1840,11 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_debugger_provider(&mut self, provider: impl DebuggerProvider + 'static) {
|
pub fn set_debugger_provider(&mut self, provider: impl DebuggerProvider + 'static) {
|
||||||
self.debugger_provider = Some(Box::new(provider));
|
self.debugger_provider = Some(Arc::new(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debugger_provider(&self) -> Option<Arc<dyn DebuggerProvider>> {
|
||||||
|
self.debugger_provider.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialized_ssh_project(&self) -> Option<SerializedSshProject> {
|
pub fn serialized_ssh_project(&self) -> Option<SerializedSshProject> {
|
||||||
|
|
|
@ -133,6 +133,46 @@ impl Render for QuickActionBar {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let last_run_debug = self
|
||||||
|
.workspace
|
||||||
|
.read_with(cx, |workspace, cx| {
|
||||||
|
workspace
|
||||||
|
.debugger_provider()
|
||||||
|
.map(|provider| provider.debug_scenario_scheduled_last(cx))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let run_button = if last_run_debug {
|
||||||
|
QuickActionBarButton::new(
|
||||||
|
"debug",
|
||||||
|
IconName::Debug, // TODO: use debug + play icon
|
||||||
|
false,
|
||||||
|
Box::new(debugger_ui::Start),
|
||||||
|
focus_handle.clone(),
|
||||||
|
"Debug",
|
||||||
|
move |_, window, cx| {
|
||||||
|
window.dispatch_action(Box::new(debugger_ui::Start), cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let action = Box::new(tasks_ui::Spawn::ViaModal {
|
||||||
|
reveal_target: None,
|
||||||
|
});
|
||||||
|
QuickActionBarButton::new(
|
||||||
|
"run",
|
||||||
|
IconName::Play,
|
||||||
|
false,
|
||||||
|
action.boxed_clone(),
|
||||||
|
focus_handle.clone(),
|
||||||
|
"Spawn Task",
|
||||||
|
move |_, window, cx| {
|
||||||
|
window.dispatch_action(action.boxed_clone(), cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let assistant_button = QuickActionBarButton::new(
|
let assistant_button = QuickActionBarButton::new(
|
||||||
"toggle inline assistant",
|
"toggle inline assistant",
|
||||||
IconName::ZedAssistant,
|
IconName::ZedAssistant,
|
||||||
|
@ -561,6 +601,7 @@ impl Render for QuickActionBar {
|
||||||
AgentSettings::get_global(cx).enabled && AgentSettings::get_global(cx).button,
|
AgentSettings::get_global(cx).enabled && AgentSettings::get_global(cx).button,
|
||||||
|bar| bar.child(assistant_button),
|
|bar| bar.child(assistant_button),
|
||||||
)
|
)
|
||||||
|
.child(run_button)
|
||||||
.children(code_actions_dropdown)
|
.children(code_actions_dropdown)
|
||||||
.children(editor_selections_dropdown)
|
.children(editor_selections_dropdown)
|
||||||
.child(editor_settings_dropdown)
|
.child(editor_settings_dropdown)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue