Detailed changes
@@ -1,3 +1,4 @@
+use crate::persistence::DebuggerPaneItem;
use crate::{
ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack,
StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
@@ -32,8 +33,10 @@ use settings::Settings;
use std::any::TypeId;
use std::path::Path;
use std::sync::Arc;
-use task::{DebugTaskDefinition, DebugTaskTemplate};
-use terminal_view::terminal_panel::TerminalPanel;
+use task::{
+ DebugTaskDefinition, DebugTaskTemplate, HideStrategy, RevealStrategy, RevealTarget, TaskId,
+};
+use terminal_view::TerminalView;
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
use workspace::{
Workspace,
@@ -293,23 +296,21 @@ impl DebugPanel {
(session.clone(), dap_store.boot_session(session, cx))
})?;
+ Self::register_session(this.clone(), session.clone(), cx).await?;
- match task.await {
- Err(e) => {
- this.update(cx, |this, cx| {
- this.workspace
- .update(cx, |workspace, cx| {
- workspace.show_error(&e, cx);
- })
- .ok();
- })
- .ok();
+ if let Err(e) = task.await {
+ this.update(cx, |this, cx| {
+ this.workspace
+ .update(cx, |workspace, cx| {
+ workspace.show_error(&e, cx);
+ })
+ .ok();
+ })
+ .ok();
- session
- .update(cx, |session, cx| session.shutdown(cx))?
- .await;
- }
- Ok(_) => Self::register_session(this, session, cx).await?,
+ session
+ .update(cx, |session, cx| session.shutdown(cx))?
+ .await;
}
anyhow::Ok(())
@@ -467,6 +468,7 @@ impl DebugPanel {
) {
match event {
dap_store::DapStoreEvent::RunInTerminal {
+ session_id,
title,
cwd,
command,
@@ -476,6 +478,7 @@ impl DebugPanel {
..
} => {
self.handle_run_in_terminal_request(
+ *session_id,
title.clone(),
cwd.clone(),
command.clone(),
@@ -499,6 +502,7 @@ impl DebugPanel {
fn handle_run_in_terminal_request(
&self,
+ session_id: SessionId,
title: Option<String>,
cwd: Option<Arc<Path>>,
command: Option<String>,
@@ -506,56 +510,83 @@ impl DebugPanel {
envs: HashMap<String, String>,
mut sender: mpsc::Sender<Result<u32>>,
window: &mut Window,
- cx: &mut App,
+ cx: &mut Context<Self>,
) -> Task<Result<()>> {
- let terminal_task = self.workspace.update(cx, |workspace, cx| {
- let terminal_panel = workspace.panel::<TerminalPanel>(cx).ok_or_else(|| {
- anyhow!("RunInTerminal DAP request failed because TerminalPanel wasn't found")
- });
+ let Some(session) = self
+ .sessions
+ .iter()
+ .find(|s| s.read(cx).session_id(cx) == session_id)
+ else {
+ return Task::ready(Err(anyhow!("no session {:?} found", session_id)));
+ };
+ let running = session.read(cx).running_state();
+ let cwd = cwd.map(|p| p.to_path_buf());
+ let shell = self
+ .project
+ .read(cx)
+ .terminal_settings(&cwd, cx)
+ .shell
+ .clone();
+ let kind = if let Some(command) = command {
+ let title = title.clone().unwrap_or(command.clone());
+ TerminalKind::Task(task::SpawnInTerminal {
+ id: TaskId("debug".to_string()),
+ full_label: title.clone(),
+ label: title.clone(),
+ command: command.clone(),
+ args,
+ command_label: title.clone(),
+ cwd,
+ env: envs,
+ use_new_terminal: true,
+ allow_concurrent_runs: true,
+ reveal: RevealStrategy::NoFocus,
+ reveal_target: RevealTarget::Dock,
+ hide: HideStrategy::Never,
+ shell,
+ show_summary: false,
+ show_command: false,
+ show_rerun: false,
+ })
+ } else {
+ TerminalKind::Shell(cwd.map(|c| c.to_path_buf()))
+ };
- let terminal_panel = match terminal_panel {
- Ok(panel) => panel,
- Err(err) => return Task::ready(Err(err)),
- };
+ let workspace = self.workspace.clone();
+ let project = self.project.downgrade();
- terminal_panel.update(cx, |terminal_panel, cx| {
- let terminal_task = terminal_panel.add_terminal(
- TerminalKind::Debug {
- command,
- args,
- envs,
- cwd,
- title,
- },
- task::RevealStrategy::Never,
- window,
- cx,
- );
+ let terminal_task = self.project.update(cx, |project, cx| {
+ project.create_terminal(kind, window.window_handle(), cx)
+ });
+ let terminal_task = cx.spawn_in(window, async move |_, cx| {
+ let terminal = terminal_task.await?;
- cx.spawn(async move |_, cx| {
- let pid_task = async move {
- let terminal = terminal_task.await?;
+ let terminal_view = cx.new_window_entity(|window, cx| {
+ TerminalView::new(terminal.clone(), workspace, None, project, window, cx)
+ })?;
- terminal.read_with(cx, |terminal, _| terminal.pty_info.pid())
- };
+ running.update_in(cx, |running, window, cx| {
+ running.ensure_pane_item(DebuggerPaneItem::Terminal, window, cx);
+ running.debug_terminal.update(cx, |debug_terminal, cx| {
+ debug_terminal.terminal = Some(terminal_view);
+ cx.notify();
+ });
+ })?;
- pid_task.await
- })
- })
+ anyhow::Ok(terminal.read_with(cx, |terminal, _| terminal.pty_info.pid())?)
});
cx.background_spawn(async move {
- match terminal_task {
- Ok(pid_task) => match pid_task.await {
- Ok(Some(pid)) => sender.send(Ok(pid.as_u32())).await?,
- Ok(None) => {
+ match terminal_task.await {
+ Ok(pid_task) => match pid_task {
+ Some(pid) => sender.send(Ok(pid.as_u32())).await?,
+ None => {
sender
.send(Err(anyhow!(
"Terminal was spawned but PID was not available"
)))
.await?
}
- Err(error) => sender.send(Err(anyhow!(error))).await?,
},
Err(error) => sender.send(Err(anyhow!(error))).await?,
};
@@ -9,7 +9,7 @@ use util::ResultExt;
use workspace::{Member, Pane, PaneAxis, Workspace};
use crate::session::running::{
- self, RunningState, SubView, breakpoint_list::BreakpointList, console::Console,
+ self, DebugTerminal, RunningState, SubView, breakpoint_list::BreakpointList, console::Console,
loaded_source_list::LoadedSourceList, module_list::ModuleList,
stack_frame_list::StackFrameList, variable_list::VariableList,
};
@@ -22,6 +22,7 @@ pub(crate) enum DebuggerPaneItem {
Frames,
Modules,
LoadedSources,
+ Terminal,
}
impl DebuggerPaneItem {
@@ -33,6 +34,7 @@ impl DebuggerPaneItem {
DebuggerPaneItem::Frames,
DebuggerPaneItem::Modules,
DebuggerPaneItem::LoadedSources,
+ DebuggerPaneItem::Terminal,
];
VARIANTS
}
@@ -55,6 +57,7 @@ impl DebuggerPaneItem {
DebuggerPaneItem::Frames => SharedString::new_static("Frames"),
DebuggerPaneItem::Modules => SharedString::new_static("Modules"),
DebuggerPaneItem::LoadedSources => SharedString::new_static("Sources"),
+ DebuggerPaneItem::Terminal => SharedString::new_static("Terminal"),
}
}
}
@@ -169,6 +172,7 @@ pub(crate) fn deserialize_pane_layout(
console: &Entity<Console>,
breakpoint_list: &Entity<BreakpointList>,
loaded_sources: &Entity<LoadedSourceList>,
+ terminal: &Entity<DebugTerminal>,
subscriptions: &mut HashMap<EntityId, Subscription>,
window: &mut Window,
cx: &mut Context<RunningState>,
@@ -191,6 +195,7 @@ pub(crate) fn deserialize_pane_layout(
console,
breakpoint_list,
loaded_sources,
+ terminal,
subscriptions,
window,
cx,
@@ -273,6 +278,13 @@ pub(crate) fn deserialize_pane_layout(
})),
cx,
)),
+ DebuggerPaneItem::Terminal => Box::new(SubView::new(
+ pane.focus_handle(cx),
+ terminal.clone().into(),
+ DebuggerPaneItem::Terminal,
+ None,
+ cx,
+ )),
})
.collect();
@@ -104,6 +104,12 @@ impl DebugSession {
&self.mode
}
+ pub(crate) fn running_state(&self) -> Entity<RunningState> {
+ match &self.mode {
+ DebugSessionState::Running(running_state) => running_state.clone(),
+ }
+ }
+
pub(crate) fn label(&self, cx: &App) -> String {
if let Some(label) = self.label.get() {
return label.to_owned();
@@ -27,6 +27,7 @@ use project::{
use rpc::proto::ViewId;
use settings::Settings;
use stack_frame_list::StackFrameList;
+use terminal_view::TerminalView;
use ui::{
ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,
InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
@@ -35,7 +36,7 @@ use ui::{
use util::ResultExt;
use variable_list::VariableList;
use workspace::{
- ActivePaneDecorator, DraggedTab, Item, Member, Pane, PaneGroup, Workspace,
+ ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, Workspace,
item::TabContentParams, move_item, pane::Event,
};
@@ -50,6 +51,7 @@ pub struct RunningState {
_subscriptions: Vec<Subscription>,
stack_frame_list: Entity<stack_frame_list::StackFrameList>,
loaded_sources_list: Entity<LoadedSourceList>,
+ pub debug_terminal: Entity<DebugTerminal>,
module_list: Entity<module_list::ModuleList>,
_console: Entity<Console>,
breakpoint_list: Entity<BreakpointList>,
@@ -364,6 +366,40 @@ pub(crate) fn new_debugger_pane(
ret
}
+
+pub struct DebugTerminal {
+ pub terminal: Option<Entity<TerminalView>>,
+ focus_handle: FocusHandle,
+}
+
+impl DebugTerminal {
+ fn empty(cx: &mut Context<Self>) -> Self {
+ Self {
+ terminal: None,
+ focus_handle: cx.focus_handle(),
+ }
+ }
+}
+
+impl gpui::Render for DebugTerminal {
+ fn render(&mut self, _window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
+ if let Some(terminal) = self.terminal.clone() {
+ terminal.into_any_element()
+ } else {
+ div().track_focus(&self.focus_handle).into_any_element()
+ }
+ }
+}
+impl Focusable for DebugTerminal {
+ fn focus_handle(&self, cx: &App) -> FocusHandle {
+ if let Some(terminal) = self.terminal.as_ref() {
+ return terminal.focus_handle(cx);
+ } else {
+ self.focus_handle.clone()
+ }
+ }
+}
+
impl RunningState {
pub fn new(
session: Entity<Session>,
@@ -380,6 +416,8 @@ impl RunningState {
StackFrameList::new(workspace.clone(), session.clone(), weak_state, window, cx)
});
+ let debug_terminal = cx.new(DebugTerminal::empty);
+
let variable_list =
cx.new(|cx| VariableList::new(session.clone(), stack_frame_list.clone(), window, cx));
@@ -452,6 +490,7 @@ impl RunningState {
&console,
&breakpoint_list,
&loaded_source_list,
+ &debug_terminal,
&mut pane_close_subscriptions,
window,
cx,
@@ -494,6 +533,7 @@ impl RunningState {
breakpoint_list,
loaded_sources_list: loaded_source_list,
pane_close_subscriptions,
+ debug_terminal,
_schedule_serialize: None,
}
}
@@ -525,6 +565,90 @@ impl RunningState {
self.panes.pane_at_pixel_position(position).is_some()
}
+ fn create_sub_view(
+ &self,
+ item_kind: DebuggerPaneItem,
+ pane: &Entity<Pane>,
+ cx: &mut Context<Self>,
+ ) -> Box<dyn ItemHandle> {
+ match item_kind {
+ DebuggerPaneItem::Console => {
+ let weak_console = self._console.clone().downgrade();
+
+ Box::new(SubView::new(
+ pane.focus_handle(cx),
+ self._console.clone().into(),
+ item_kind,
+ Some(Box::new(move |cx| {
+ weak_console
+ .read_with(cx, |console, cx| console.show_indicator(cx))
+ .unwrap_or_default()
+ })),
+ cx,
+ ))
+ }
+ DebuggerPaneItem::Variables => Box::new(SubView::new(
+ self.variable_list.focus_handle(cx),
+ self.variable_list.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ DebuggerPaneItem::BreakpointList => Box::new(SubView::new(
+ self.breakpoint_list.focus_handle(cx),
+ self.breakpoint_list.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ DebuggerPaneItem::Frames => Box::new(SubView::new(
+ self.stack_frame_list.focus_handle(cx),
+ self.stack_frame_list.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ DebuggerPaneItem::Modules => Box::new(SubView::new(
+ self.module_list.focus_handle(cx),
+ self.module_list.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ DebuggerPaneItem::LoadedSources => Box::new(SubView::new(
+ self.loaded_sources_list.focus_handle(cx),
+ self.loaded_sources_list.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ DebuggerPaneItem::Terminal => Box::new(SubView::new(
+ self.debug_terminal.focus_handle(cx),
+ self.debug_terminal.clone().into(),
+ item_kind,
+ None,
+ cx,
+ )),
+ }
+ }
+
+ pub(crate) fn ensure_pane_item(
+ &mut self,
+ item_kind: DebuggerPaneItem,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if self.pane_items_status(cx).get(&item_kind) == Some(&true) {
+ return;
+ };
+ let pane = self.panes.last_pane();
+ let sub_view = self.create_sub_view(item_kind, &pane, cx);
+
+ pane.update(cx, |pane, cx| {
+ pane.add_item_inner(sub_view, false, false, false, None, window, cx);
+ })
+ }
+
pub(crate) fn add_pane_item(
&mut self,
item_kind: DebuggerPaneItem,
@@ -538,58 +662,7 @@ impl RunningState {
);
if let Some(pane) = self.panes.pane_at_pixel_position(position) {
- let sub_view = match item_kind {
- DebuggerPaneItem::Console => {
- let weak_console = self._console.clone().downgrade();
-
- Box::new(SubView::new(
- pane.focus_handle(cx),
- self._console.clone().into(),
- item_kind,
- Some(Box::new(move |cx| {
- weak_console
- .read_with(cx, |console, cx| console.show_indicator(cx))
- .unwrap_or_default()
- })),
- cx,
- ))
- }
- DebuggerPaneItem::Variables => Box::new(SubView::new(
- self.variable_list.focus_handle(cx),
- self.variable_list.clone().into(),
- item_kind,
- None,
- cx,
- )),
- DebuggerPaneItem::BreakpointList => Box::new(SubView::new(
- self.breakpoint_list.focus_handle(cx),
- self.breakpoint_list.clone().into(),
- item_kind,
- None,
- cx,
- )),
- DebuggerPaneItem::Frames => Box::new(SubView::new(
- self.stack_frame_list.focus_handle(cx),
- self.stack_frame_list.clone().into(),
- item_kind,
- None,
- cx,
- )),
- DebuggerPaneItem::Modules => Box::new(SubView::new(
- self.module_list.focus_handle(cx),
- self.module_list.clone().into(),
- item_kind,
- None,
- cx,
- )),
- DebuggerPaneItem::LoadedSources => Box::new(SubView::new(
- self.loaded_sources_list.focus_handle(cx),
- self.loaded_sources_list.clone().into(),
- item_kind,
- None,
- cx,
- )),
- };
+ let sub_view = self.create_sub_view(item_kind, pane, cx);
pane.update(cx, |pane, cx| {
pane.add_item(sub_view, false, false, None, window, cx);
@@ -1,4 +1,4 @@
-use crate::{tests::start_debug_session, *};
+use crate::{persistence::DebuggerPaneItem, tests::start_debug_session, *};
use dap::{
ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint,
StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
@@ -25,7 +25,7 @@ use std::{
atomic::{AtomicBool, Ordering},
},
};
-use terminal_view::{TerminalView, terminal_panel::TerminalPanel};
+use terminal_view::terminal_panel::TerminalPanel;
use tests::{active_debug_session_panel, init_test, init_test_workspace};
use util::path;
use workspace::{Item, dock::Panel};
@@ -385,22 +385,17 @@ async fn test_handle_successful_run_in_terminal_reverse_request(
workspace
.update(cx, |workspace, _window, cx| {
- let terminal_panel = workspace.panel::<TerminalPanel>(cx).unwrap();
-
- let panel = terminal_panel.read(cx).pane().unwrap().read(cx);
-
- assert_eq!(1, panel.items_len());
- assert!(
- panel
- .active_item()
- .unwrap()
- .downcast::<TerminalView>()
- .unwrap()
- .read(cx)
- .terminal()
+ let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
+ let session = debug_panel.read(cx).active_session().unwrap();
+ let running = session.read(cx).running_state();
+ assert_eq!(
+ running
.read(cx)
- .debug_terminal()
+ .pane_items_status(cx)
+ .get(&DebuggerPaneItem::Terminal),
+ Some(&true)
);
+ assert!(running.read(cx).debug_terminal.read(cx).terminal.is_some());
})
.unwrap();
@@ -31,14 +31,6 @@ pub enum TerminalKind {
Shell(Option<PathBuf>),
/// Run a task.
Task(SpawnInTerminal),
- /// Run a debug terminal.
- Debug {
- command: Option<String>,
- args: Vec<String>,
- envs: HashMap<String, String>,
- cwd: Option<Arc<Path>>,
- title: Option<String>,
- },
}
/// SshCommand describes how to connect to a remote server
@@ -105,7 +97,6 @@ impl Project {
self.active_project_directory(cx)
}
}
- TerminalKind::Debug { cwd, .. } => cwd.clone(),
};
let mut settings_location = None;
@@ -209,7 +200,6 @@ impl Project {
this.active_project_directory(cx)
}
}
- TerminalKind::Debug { cwd, .. } => cwd.clone(),
};
let ssh_details = this.ssh_details(cx);
@@ -243,7 +233,6 @@ impl Project {
};
let mut python_venv_activate_command = None;
- let debug_terminal = matches!(kind, TerminalKind::Debug { .. });
let (spawn_task, shell) = match kind {
TerminalKind::Shell(_) => {
@@ -339,27 +328,6 @@ impl Project {
}
}
}
- TerminalKind::Debug {
- command,
- args,
- envs,
- title,
- ..
- } => {
- env.extend(envs);
-
- let shell = if let Some(program) = command {
- Shell::WithArguments {
- program,
- args,
- title_override: Some(title.unwrap_or("Debug Terminal".into()).into()),
- }
- } else {
- settings.shell.clone()
- };
-
- (None, shell)
- }
};
TerminalBuilder::new(
local_path.map(|path| path.to_path_buf()),
@@ -373,7 +341,6 @@ impl Project {
ssh_details.is_some(),
window,
completion_tx,
- debug_terminal,
cx,
)
.map(|builder| {
@@ -352,7 +352,6 @@ impl TerminalBuilder {
is_ssh_terminal: bool,
window: AnyWindowHandle,
completion_tx: Sender<Option<ExitStatus>>,
- debug_terminal: bool,
cx: &App,
) -> Result<TerminalBuilder> {
// If the parent environment doesn't have a locale set
@@ -502,7 +501,6 @@ impl TerminalBuilder {
word_regex: RegexSearch::new(WORD_REGEX).unwrap(),
python_file_line_regex: RegexSearch::new(PYTHON_FILE_LINE_REGEX).unwrap(),
vi_mode_enabled: false,
- debug_terminal,
is_ssh_terminal,
python_venv_directory,
};
@@ -660,7 +658,6 @@ pub struct Terminal {
python_file_line_regex: RegexSearch,
task: Option<TaskState>,
vi_mode_enabled: bool,
- debug_terminal: bool,
is_ssh_terminal: bool,
}
@@ -1855,10 +1852,6 @@ impl Terminal {
self.task.as_ref()
}
- pub fn debug_terminal(&self) -> bool {
- self.debug_terminal
- }
-
pub fn wait_for_completed_task(&self, cx: &App) -> Task<Option<ExitStatus>> {
if let Some(task) = self.task() {
if task.status == TaskStatus::Running {
@@ -1420,9 +1420,6 @@ impl Item for TerminalView {
}
}
},
- None if self.terminal.read(cx).debug_terminal() => {
- (IconName::Debug, Color::Muted, None)
- }
None => (IconName::Terminal, Color::Muted, None),
};
@@ -1583,7 +1580,7 @@ impl SerializableItem for TerminalView {
cx: &mut Context<Self>,
) -> Option<Task<gpui::Result<()>>> {
let terminal = self.terminal().read(cx);
- if terminal.task().is_some() || terminal.debug_terminal() {
+ if terminal.task().is_some() {
return None;
}
@@ -142,6 +142,10 @@ impl PaneGroup {
self.root.first_pane()
}
+ pub fn last_pane(&self) -> Entity<Pane> {
+ self.root.last_pane()
+ }
+
pub fn find_pane_in_direction(
&mut self,
active_pane: &Entity<Pane>,
@@ -360,6 +364,13 @@ impl Member {
}
}
+ fn last_pane(&self) -> Entity<Pane> {
+ match self {
+ Member::Axis(axis) => axis.members.last().unwrap().last_pane(),
+ Member::Pane(pane) => pane.clone(),
+ }
+ }
+
pub fn render(
&self,
basis: usize,