Add a run menu (#32505)
As part of this I refactored the logic that enabled/disabled actions in the debugger to happen at action registration time instead of using command palette filters. This allows the menu to grey out actions correctly. Release Notes: - Add a "Run" menu to contain tasks and debugger
This commit is contained in:
parent
444f797827
commit
00a8101016
12 changed files with 296 additions and 369 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4695,7 +4695,6 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette_hooks",
|
|
||||||
"convert_case 0.8.0",
|
"convert_case 0.8.0",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dap",
|
"dap",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Static tasks configuration.
|
// Project tasks configuration. See https://zed.dev/docs/tasks for documentation.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
[
|
[
|
||||||
|
|
|
@ -4,12 +4,10 @@ use crate::session::running::RunningState;
|
||||||
use crate::{
|
use crate::{
|
||||||
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
|
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
|
||||||
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, NewProcessModal,
|
FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, NewProcessModal,
|
||||||
NewProcessMode, Pause, Restart, ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop,
|
NewProcessMode, Pause, Restart, StepInto, StepOut, StepOver, Stop, ToggleExpandItem,
|
||||||
ToggleExpandItem, ToggleIgnoreBreakpoints, ToggleSessionPicker, ToggleThreadPicker,
|
ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal,
|
||||||
persistence, spawn_task_or_modal,
|
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
|
||||||
use dap::adapters::DebugAdapterName;
|
use dap::adapters::DebugAdapterName;
|
||||||
use dap::debugger_settings::DebugPanelDockPosition;
|
use dap::debugger_settings::DebugPanelDockPosition;
|
||||||
use dap::{
|
use dap::{
|
||||||
|
@ -29,7 +27,6 @@ use project::{Fs, WorktreeId};
|
||||||
use project::{Project, debugger::session::ThreadStatus};
|
use project::{Project, debugger::session::ThreadStatus};
|
||||||
use rpc::proto::{self};
|
use rpc::proto::{self};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::any::TypeId;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use task::{DebugScenario, TaskContext};
|
use task::{DebugScenario, TaskContext};
|
||||||
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};
|
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};
|
||||||
|
@ -140,82 +137,6 @@ impl DebugPanel {
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
.map(|session| session.read(cx).running_state().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn filter_action_types(&self, cx: &mut App) {
|
|
||||||
let (has_active_session, supports_restart, support_step_back, status) = self
|
|
||||||
.active_session()
|
|
||||||
.map(|item| {
|
|
||||||
let running = item.read(cx).running_state().clone();
|
|
||||||
let caps = running.read(cx).capabilities(cx);
|
|
||||||
(
|
|
||||||
!running.read(cx).session().read(cx).is_terminated(),
|
|
||||||
caps.supports_restart_request.unwrap_or_default(),
|
|
||||||
caps.supports_step_back.unwrap_or_default(),
|
|
||||||
running.read(cx).thread_status(cx),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or((false, false, false, None));
|
|
||||||
|
|
||||||
let filter = CommandPaletteFilter::global_mut(cx);
|
|
||||||
let debugger_action_types = [
|
|
||||||
TypeId::of::<Detach>(),
|
|
||||||
TypeId::of::<Stop>(),
|
|
||||||
TypeId::of::<ToggleIgnoreBreakpoints>(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let running_action_types = [TypeId::of::<Pause>()];
|
|
||||||
|
|
||||||
let stopped_action_type = [
|
|
||||||
TypeId::of::<Continue>(),
|
|
||||||
TypeId::of::<StepOver>(),
|
|
||||||
TypeId::of::<StepInto>(),
|
|
||||||
TypeId::of::<StepOut>(),
|
|
||||||
TypeId::of::<ShowStackTrace>(),
|
|
||||||
TypeId::of::<editor::actions::DebuggerRunToCursor>(),
|
|
||||||
TypeId::of::<editor::actions::DebuggerEvaluateSelectedText>(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let step_back_action_type = [TypeId::of::<StepBack>()];
|
|
||||||
let restart_action_type = [TypeId::of::<Restart>()];
|
|
||||||
|
|
||||||
if has_active_session {
|
|
||||||
filter.show_action_types(debugger_action_types.iter());
|
|
||||||
|
|
||||||
if supports_restart {
|
|
||||||
filter.show_action_types(restart_action_type.iter());
|
|
||||||
} else {
|
|
||||||
filter.hide_action_types(&restart_action_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if support_step_back {
|
|
||||||
filter.show_action_types(step_back_action_type.iter());
|
|
||||||
} else {
|
|
||||||
filter.hide_action_types(&step_back_action_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
match status {
|
|
||||||
Some(ThreadStatus::Running) => {
|
|
||||||
filter.show_action_types(running_action_types.iter());
|
|
||||||
filter.hide_action_types(&stopped_action_type);
|
|
||||||
}
|
|
||||||
Some(ThreadStatus::Stopped) => {
|
|
||||||
filter.show_action_types(stopped_action_type.iter());
|
|
||||||
filter.hide_action_types(&running_action_types);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
filter.hide_action_types(&running_action_types);
|
|
||||||
filter.hide_action_types(&stopped_action_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// show only the `debug: start`
|
|
||||||
filter.hide_action_types(&debugger_action_types);
|
|
||||||
filter.hide_action_types(&step_back_action_type);
|
|
||||||
filter.hide_action_types(&restart_action_type);
|
|
||||||
filter.hide_action_types(&running_action_types);
|
|
||||||
filter.hide_action_types(&stopped_action_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load(
|
pub fn load(
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
cx: &mut AsyncWindowContext,
|
cx: &mut AsyncWindowContext,
|
||||||
|
@ -233,17 +154,6 @@ impl DebugPanel {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.observe_new::<DebugPanel>(|debug_panel, _, cx| {
|
|
||||||
Self::filter_action_types(debug_panel, cx);
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
cx.observe(&debug_panel, |_, debug_panel, cx| {
|
|
||||||
debug_panel.update(cx, |debug_panel, cx| {
|
|
||||||
Self::filter_action_types(debug_panel, cx);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
workspace.set_debugger_provider(DebuggerProvider(debug_panel.clone()));
|
workspace.set_debugger_provider(DebuggerProvider(debug_panel.clone()));
|
||||||
|
|
||||||
debug_panel
|
debug_panel
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
use std::any::TypeId;
|
||||||
|
|
||||||
use dap::debugger_settings::DebuggerSettings;
|
use dap::debugger_settings::DebuggerSettings;
|
||||||
use debugger_panel::{DebugPanel, ToggleFocus};
|
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, DispatchPhase, EntityInputHandler, actions};
|
||||||
use new_process_modal::{NewProcessModal, NewProcessMode};
|
use new_process_modal::{NewProcessModal, NewProcessMode};
|
||||||
use project::debugger::{self, breakpoint_store::SourceBreakpoint};
|
use project::debugger::{self, breakpoint_store::SourceBreakpoint, session::ThreadStatus};
|
||||||
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 tasks_ui::{Spawn, TaskOverrides};
|
||||||
|
use ui::{FluentBuilder, InteractiveElement};
|
||||||
use util::maybe;
|
use util::maybe;
|
||||||
use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
|
use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
|
||||||
|
|
||||||
|
@ -68,148 +71,6 @@ pub fn init(cx: &mut App) {
|
||||||
.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);
|
||||||
})
|
})
|
||||||
.register_action(|workspace, _: &Pause, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.pause_thread(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &Restart, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.restart_session(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &Continue, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.continue_thread(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &StepInto, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.step_in(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &StepOver, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.step_over(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &StepOut, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel.read_with(cx, |panel, cx| {
|
|
||||||
panel
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
}) {
|
|
||||||
active_item.update(cx, |item, cx| item.step_out(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &StepBack, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.step_back(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &Stop, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
cx.defer(move |cx| {
|
|
||||||
active_item.update(cx, |item, cx| item.stop_thread(cx))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(|workspace, _: &ToggleIgnoreBreakpoints, _, cx| {
|
|
||||||
if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {
|
|
||||||
if let Some(active_item) = debug_panel
|
|
||||||
.read(cx)
|
|
||||||
.active_session()
|
|
||||||
.map(|session| session.read(cx).running_state().clone())
|
|
||||||
{
|
|
||||||
active_item.update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register_action(
|
|
||||||
|workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
|
|
||||||
workspace.project().update(cx, |project, cx| {
|
|
||||||
project.dap_store().update(cx, |store, cx| {
|
|
||||||
store.shutdown_sessions(cx).detach();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.register_action(
|
|
||||||
|workspace: &mut Workspace, _: &ShowStackTrace, window, cx| {
|
|
||||||
let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx) {
|
|
||||||
let is_active = workspace
|
|
||||||
.active_item(cx)
|
|
||||||
.is_some_and(|item| item.item_id() == existing.item_id());
|
|
||||||
workspace.activate_item(&existing, true, !is_active, window, cx);
|
|
||||||
} else {
|
|
||||||
let Some(active_session) = debug_panel.read(cx).active_session() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let project = workspace.project();
|
|
||||||
|
|
||||||
let stack_trace_view = active_session.update(cx, |session, cx| {
|
|
||||||
session.stack_trace_view(project, window, cx).clone()
|
|
||||||
});
|
|
||||||
|
|
||||||
workspace.add_item_to_active_pane(
|
|
||||||
Box::new(stack_trace_view),
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
|
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
|
||||||
NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx);
|
NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx);
|
||||||
})
|
})
|
||||||
|
@ -223,26 +84,180 @@ pub fn init(cx: &mut App) {
|
||||||
debug_panel.rerun_last_session(workspace, window, cx);
|
debug_panel.rerun_last_session(workspace, window, cx);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.register_action(
|
||||||
|
|workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
|
||||||
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
project.dap_store().update(cx, |store, cx| {
|
||||||
|
store.shutdown_sessions(cx).detach();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.register_action_renderer(|div, workspace, _, cx| {
|
||||||
|
let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
let Some(active_item) = debug_panel
|
||||||
|
.read(cx)
|
||||||
|
.active_session()
|
||||||
|
.map(|session| session.read(cx).running_state().clone())
|
||||||
|
else {
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
let running_state = active_item.read(cx);
|
||||||
|
if running_state.session().read(cx).is_terminated() {
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
let caps = running_state.capabilities(cx);
|
||||||
|
let supports_restart = caps.supports_restart_request.unwrap_or_default();
|
||||||
|
let supports_step_back = caps.supports_step_back.unwrap_or_default();
|
||||||
|
let status = running_state.thread_status(cx);
|
||||||
|
|
||||||
|
let active_item = active_item.downgrade();
|
||||||
|
div.when(status == Some(ThreadStatus::Running), |div| {
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
div.on_action(move |_: &Pause, _, cx| {
|
||||||
|
active_item
|
||||||
|
.update(cx, |item, cx| item.pause_thread(cx))
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.when(status == Some(ThreadStatus::Stopped), |div| {
|
||||||
|
div.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &StepInto, _, cx| {
|
||||||
|
active_item.update(cx, |item, cx| item.step_in(cx)).ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &StepOver, _, cx| {
|
||||||
|
active_item.update(cx, |item, cx| item.step_over(cx)).ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &StepOut, _, cx| {
|
||||||
|
active_item.update(cx, |item, cx| item.step_out(cx)).ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.when(supports_step_back, |div| {
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
div.on_action(move |_: &StepBack, _, cx| {
|
||||||
|
active_item.update(cx, |item, cx| item.step_back(cx)).ok();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &Continue, _, cx| {
|
||||||
|
active_item
|
||||||
|
.update(cx, |item, cx| item.continue_thread(cx))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_action(cx.listener(
|
||||||
|
|workspace, _: &ShowStackTrace, window, cx| {
|
||||||
|
let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx)
|
||||||
|
{
|
||||||
|
let is_active = workspace
|
||||||
|
.active_item(cx)
|
||||||
|
.is_some_and(|item| item.item_id() == existing.item_id());
|
||||||
|
workspace
|
||||||
|
.activate_item(&existing, true, !is_active, window, cx);
|
||||||
|
} else {
|
||||||
|
let Some(active_session) =
|
||||||
|
debug_panel.read(cx).active_session()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let project = workspace.project();
|
||||||
|
|
||||||
|
let stack_trace_view =
|
||||||
|
active_session.update(cx, |session, cx| {
|
||||||
|
session.stack_trace_view(project, window, cx).clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
workspace.add_item_to_active_pane(
|
||||||
|
Box::new(stack_trace_view),
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.when(supports_restart, |div| {
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
div.on_action(move |_: &Restart, _, cx| {
|
||||||
|
active_item
|
||||||
|
.update(cx, |item, cx| item.restart_session(cx))
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &Stop, _, cx| {
|
||||||
|
active_item.update(cx, |item, cx| item.stop_thread(cx)).ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_action({
|
||||||
|
let active_item = active_item.clone();
|
||||||
|
move |_: &ToggleIgnoreBreakpoints, _, cx| {
|
||||||
|
active_item
|
||||||
|
.update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.observe_new({
|
cx.observe_new({
|
||||||
move |editor: &mut Editor, _, cx| {
|
move |editor: &mut Editor, _, _| {
|
||||||
editor
|
editor
|
||||||
.register_action(cx.listener(
|
.register_action_renderer(move |editor, window, cx| {
|
||||||
move |editor, _: &editor::actions::DebuggerRunToCursor, _, cx| {
|
let Some(workspace) = editor.workspace() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(debug_panel) = workspace.read(cx).panel::<DebugPanel>(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(active_session) = debug_panel
|
||||||
|
.clone()
|
||||||
|
.update(cx, |panel, _| panel.active_session())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let editor = cx.entity().downgrade();
|
||||||
|
window.on_action(TypeId::of::<editor::actions::RunToCursor>(), {
|
||||||
|
let editor = editor.clone();
|
||||||
|
let active_session = active_session.clone();
|
||||||
|
move |_, phase, _, cx| {
|
||||||
|
if phase != DispatchPhase::Bubble {
|
||||||
|
return;
|
||||||
|
}
|
||||||
maybe!({
|
maybe!({
|
||||||
let debug_panel =
|
|
||||||
editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
|
|
||||||
let cursor_point: language::Point = editor.selections.newest(cx).head();
|
|
||||||
let active_session = debug_panel.read(cx).active_session()?;
|
|
||||||
|
|
||||||
let (buffer, position, _) = editor
|
let (buffer, position, _) = editor
|
||||||
|
.update(cx, |editor, cx| {
|
||||||
|
let cursor_point: language::Point =
|
||||||
|
editor.selections.newest(cx).head();
|
||||||
|
|
||||||
|
editor
|
||||||
.buffer()
|
.buffer()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.point_to_buffer_point(cursor_point, cx)?;
|
.point_to_buffer_point(cursor_point, cx)
|
||||||
|
})
|
||||||
|
.ok()??;
|
||||||
|
|
||||||
let path =
|
let path =
|
||||||
debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
|
debugger::breakpoint_store::BreakpointStore::abs_path_from_buffer(
|
||||||
|
@ -274,31 +289,32 @@ pub fn init(cx: &mut App) {
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
))
|
});
|
||||||
.detach();
|
|
||||||
|
|
||||||
editor
|
window.on_action(
|
||||||
.register_action(cx.listener(
|
TypeId::of::<editor::actions::EvaluateSelectedText>(),
|
||||||
move |editor, _: &editor::actions::DebuggerEvaluateSelectedText, window, cx| {
|
move |_, _, window, cx| {
|
||||||
maybe!({
|
maybe!({
|
||||||
let debug_panel =
|
let text = editor
|
||||||
editor.workspace()?.read(cx).panel::<DebugPanel>(cx)?;
|
.update(cx, |editor, cx| {
|
||||||
let active_session = debug_panel.read(cx).active_session()?;
|
editor.text_for_range(
|
||||||
|
|
||||||
let text = editor.text_for_range(
|
|
||||||
editor.selections.newest(cx).range(),
|
editor.selections.newest(cx).range(),
|
||||||
&mut None,
|
&mut None,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)?;
|
)
|
||||||
|
})
|
||||||
|
.ok()??;
|
||||||
|
|
||||||
active_session.update(cx, |session, cx| {
|
active_session.update(cx, |session, cx| {
|
||||||
session.running_state().update(cx, |state, cx| {
|
session.running_state().update(cx, |state, cx| {
|
||||||
let stack_id = state.selected_stack_frame_id(cx);
|
let stack_id = state.selected_stack_frame_id(cx);
|
||||||
|
|
||||||
state.session().update(cx, |session, cx| {
|
state.session().update(cx, |session, cx| {
|
||||||
session.evaluate(text, None, stack_id, None, cx).detach();
|
session
|
||||||
|
.evaluate(text, None, stack_id, None, cx)
|
||||||
|
.detach();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -306,7 +322,8 @@ pub fn init(cx: &mut App) {
|
||||||
Some(())
|
Some(())
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
))
|
);
|
||||||
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,7 +35,6 @@ assets.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
|
||||||
convert_case.workspace = true
|
convert_case.workspace = true
|
||||||
dap.workspace = true
|
dap.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
|
|
|
@ -243,6 +243,8 @@ impl_actions!(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
actions!(debugger, [RunToCursor, EvaluateSelectedText]);
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
editor,
|
editor,
|
||||||
[
|
[
|
||||||
|
@ -426,8 +428,6 @@ actions!(
|
||||||
DisableBreakpoint,
|
DisableBreakpoint,
|
||||||
EnableBreakpoint,
|
EnableBreakpoint,
|
||||||
EditLogBreakpoint,
|
EditLogBreakpoint,
|
||||||
DebuggerRunToCursor,
|
|
||||||
DebuggerEvaluateSelectedText,
|
|
||||||
ToggleAutoSignatureHelp,
|
ToggleAutoSignatureHelp,
|
||||||
ToggleGitBlameInline,
|
ToggleGitBlameInline,
|
||||||
OpenGitBlameCommit,
|
OpenGitBlameCommit,
|
||||||
|
|
|
@ -1054,8 +1054,9 @@ pub struct Editor {
|
||||||
style: Option<EditorStyle>,
|
style: Option<EditorStyle>,
|
||||||
text_style_refinement: Option<TextStyleRefinement>,
|
text_style_refinement: Option<TextStyleRefinement>,
|
||||||
next_editor_action_id: EditorActionId,
|
next_editor_action_id: EditorActionId,
|
||||||
editor_actions:
|
editor_actions: Rc<
|
||||||
Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
|
RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
|
||||||
|
>,
|
||||||
use_autoclose: bool,
|
use_autoclose: bool,
|
||||||
use_auto_surround: bool,
|
use_auto_surround: bool,
|
||||||
auto_replace_emoji_shortcode: bool,
|
auto_replace_emoji_shortcode: bool,
|
||||||
|
@ -7541,8 +7542,7 @@ impl Editor {
|
||||||
"Set Breakpoint"
|
"Set Breakpoint"
|
||||||
};
|
};
|
||||||
|
|
||||||
let run_to_cursor = command_palette_hooks::CommandPaletteFilter::try_global(cx)
|
let run_to_cursor = window.is_action_available(&RunToCursor, cx);
|
||||||
.map_or(false, |filter| !filter.is_hidden(&DebuggerRunToCursor));
|
|
||||||
|
|
||||||
let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
|
let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
|
||||||
BreakpointState::Enabled => Some("Disable"),
|
BreakpointState::Enabled => Some("Disable"),
|
||||||
|
@ -7566,7 +7566,7 @@ impl Editor {
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
window.dispatch_action(Box::new(DebuggerRunToCursor), cx);
|
window.dispatch_action(Box::new(RunToCursor), cx);
|
||||||
})
|
})
|
||||||
.separator()
|
.separator()
|
||||||
})
|
})
|
||||||
|
@ -19819,6 +19819,21 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_action_renderer(
|
||||||
|
&mut self,
|
||||||
|
listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
|
||||||
|
) -> Subscription {
|
||||||
|
let id = self.next_editor_action_id.post_inc();
|
||||||
|
self.editor_actions
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(id, Box::new(listener));
|
||||||
|
|
||||||
|
let editor_actions = self.editor_actions.clone();
|
||||||
|
Subscription::new(move || {
|
||||||
|
editor_actions.borrow_mut().remove(&id);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_action<A: Action>(
|
pub fn register_action<A: Action>(
|
||||||
&mut self,
|
&mut self,
|
||||||
listener: impl Fn(&A, &mut Window, &mut App) + 'static,
|
listener: impl Fn(&A, &mut Window, &mut App) + 'static,
|
||||||
|
@ -19827,7 +19842,7 @@ impl Editor {
|
||||||
let listener = Arc::new(listener);
|
let listener = Arc::new(listener);
|
||||||
self.editor_actions.borrow_mut().insert(
|
self.editor_actions.borrow_mut().insert(
|
||||||
id,
|
id,
|
||||||
Box::new(move |window, _| {
|
Box::new(move |_, window, _| {
|
||||||
let listener = listener.clone();
|
let listener = listener.clone();
|
||||||
window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
|
window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
|
||||||
let action = action.downcast_ref().unwrap();
|
let action = action.downcast_ref().unwrap();
|
||||||
|
|
|
@ -187,7 +187,7 @@ impl EditorElement {
|
||||||
let editor = &self.editor;
|
let editor = &self.editor;
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
for action in editor.editor_actions.borrow().values() {
|
for action in editor.editor_actions.borrow().values() {
|
||||||
(action)(window, cx)
|
(action)(editor, window, cx)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Copy, CopyAndTrim, CopyPermalinkToLine, Cut, DebuggerEvaluateSelectedText, DisplayPoint,
|
Copy, CopyAndTrim, CopyPermalinkToLine, Cut, DisplayPoint, DisplaySnapshot, Editor,
|
||||||
DisplaySnapshot, Editor, FindAllReferences, GoToDeclaration, GoToDefinition,
|
EvaluateSelectedText, FindAllReferences, GoToDeclaration, GoToDefinition, GoToImplementation,
|
||||||
GoToImplementation, GoToTypeDefinition, Paste, Rename, RevealInFileManager, SelectMode,
|
GoToTypeDefinition, Paste, Rename, RevealInFileManager, SelectMode, SelectionExt,
|
||||||
SelectionExt, ToDisplayPoint, ToggleCodeActions,
|
ToDisplayPoint, ToggleCodeActions,
|
||||||
actions::{Format, FormatSelections},
|
actions::{Format, FormatSelections},
|
||||||
selections_collection::SelectionsCollection,
|
selections_collection::SelectionsCollection,
|
||||||
};
|
};
|
||||||
|
@ -199,17 +199,14 @@ pub fn deploy_context_menu(
|
||||||
.is_some()
|
.is_some()
|
||||||
});
|
});
|
||||||
|
|
||||||
let evaluate_selection = command_palette_hooks::CommandPaletteFilter::try_global(cx)
|
let evaluate_selection = window.is_action_available(&EvaluateSelectedText, cx);
|
||||||
.map_or(false, |filter| {
|
|
||||||
!filter.is_hidden(&DebuggerEvaluateSelectedText)
|
|
||||||
});
|
|
||||||
|
|
||||||
ui::ContextMenu::build(window, cx, |menu, _window, _cx| {
|
ui::ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||||
let builder = menu
|
let builder = menu
|
||||||
.on_blur_subscription(Subscription::new(|| {}))
|
.on_blur_subscription(Subscription::new(|| {}))
|
||||||
.when(evaluate_selection && has_selections, |builder| {
|
.when(evaluate_selection && has_selections, |builder| {
|
||||||
builder
|
builder
|
||||||
.action("Evaluate Selection", Box::new(DebuggerEvaluateSelectedText))
|
.action("Evaluate Selection", Box::new(EvaluateSelectedText))
|
||||||
.separator()
|
.separator()
|
||||||
})
|
})
|
||||||
.action("Go to Definition", Box::new(GoToDefinition))
|
.action("Go to Definition", Box::new(GoToDefinition))
|
||||||
|
|
|
@ -922,7 +922,7 @@ type PromptForOpenPath = Box<
|
||||||
/// that can be used to register a global action to be triggered from any place in the window.
|
/// that can be used to register a global action to be triggered from any place in the window.
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
weak_self: WeakEntity<Self>,
|
weak_self: WeakEntity<Self>,
|
||||||
workspace_actions: Vec<Box<dyn Fn(Div, &mut Window, &mut Context<Self>) -> Div>>,
|
workspace_actions: Vec<Box<dyn Fn(Div, &Workspace, &mut Window, &mut Context<Self>) -> Div>>,
|
||||||
zoomed: Option<AnyWeakView>,
|
zoomed: Option<AnyWeakView>,
|
||||||
previous_dock_drag_coordinates: Option<Point<Pixels>>,
|
previous_dock_drag_coordinates: Option<Point<Pixels>>,
|
||||||
zoomed_position: Option<DockPosition>,
|
zoomed_position: Option<DockPosition>,
|
||||||
|
@ -5436,7 +5436,7 @@ impl Workspace {
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let callback = Arc::new(callback);
|
let callback = Arc::new(callback);
|
||||||
|
|
||||||
self.workspace_actions.push(Box::new(move |div, _, cx| {
|
self.workspace_actions.push(Box::new(move |div, _, _, cx| {
|
||||||
let callback = callback.clone();
|
let callback = callback.clone();
|
||||||
div.on_action(cx.listener(move |workspace, event, window, cx| {
|
div.on_action(cx.listener(move |workspace, event, window, cx| {
|
||||||
(callback)(workspace, event, window, cx)
|
(callback)(workspace, event, window, cx)
|
||||||
|
@ -5444,6 +5444,13 @@ impl Workspace {
|
||||||
}));
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub fn register_action_renderer(
|
||||||
|
&mut self,
|
||||||
|
callback: impl Fn(Div, &Workspace, &mut Window, &mut Context<Self>) -> Div + 'static,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.workspace_actions.push(Box::new(callback));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn add_workspace_actions_listeners(
|
fn add_workspace_actions_listeners(
|
||||||
&self,
|
&self,
|
||||||
|
@ -5452,7 +5459,7 @@ impl Workspace {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Div {
|
) -> Div {
|
||||||
for action in self.workspace_actions.iter() {
|
for action in self.workspace_actions.iter() {
|
||||||
div = (action)(div, window, cx)
|
div = (action)(div, self, window, cx)
|
||||||
}
|
}
|
||||||
div
|
div
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,6 +203,30 @@ pub fn app_menus() -> Vec<Menu> {
|
||||||
MenuItem::action("Previous Problem", editor::actions::GoToPreviousDiagnostic),
|
MenuItem::action("Previous Problem", editor::actions::GoToPreviousDiagnostic),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
Menu {
|
||||||
|
name: "Run".into(),
|
||||||
|
items: vec![
|
||||||
|
MenuItem::action(
|
||||||
|
"Spawn Task",
|
||||||
|
zed_actions::Spawn::ViaModal {
|
||||||
|
reveal_target: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItem::action("Start Debugger", debugger_ui::Start),
|
||||||
|
MenuItem::separator(),
|
||||||
|
MenuItem::action("Edit tasks.json...", crate::zed::OpenProjectTasks),
|
||||||
|
MenuItem::action("Edit debug.json...", crate::zed::OpenProjectDebugTasks),
|
||||||
|
MenuItem::separator(),
|
||||||
|
MenuItem::action("Continue", debugger_ui::Continue),
|
||||||
|
MenuItem::action("Step Over", debugger_ui::StepOver),
|
||||||
|
MenuItem::action("Step Into", debugger_ui::StepInto),
|
||||||
|
MenuItem::action("Step Out", debugger_ui::StepOut),
|
||||||
|
MenuItem::separator(),
|
||||||
|
MenuItem::action("Toggle Breakpoint", editor::actions::ToggleBreakpoint),
|
||||||
|
MenuItem::action("Edit Breakpoint", editor::actions::EditLogBreakpoint),
|
||||||
|
MenuItem::action("Clear all Breakpoints", debugger_ui::ClearAllBreakpoints),
|
||||||
|
],
|
||||||
|
},
|
||||||
Menu {
|
Menu {
|
||||||
name: "Window".into(),
|
name: "Window".into(),
|
||||||
items: vec![
|
items: vec![
|
||||||
|
|
|
@ -133,46 +133,6 @@ 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::PlayBug,
|
|
||||||
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::PlayAlt,
|
|
||||||
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,
|
||||||
|
@ -601,7 +561,6 @@ 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