Terminal in debugger (#29328)

- **debug-terminal**
- **Use terminal inside debugger to spawn commands**

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2025-04-24 14:26:09 -06:00 committed by GitHub
parent d3911e34de
commit c147daae4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 255 additions and 170 deletions

View file

@ -1,3 +1,4 @@
use crate::persistence::DebuggerPaneItem;
use crate::{ use crate::{
ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack, ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack,
StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
@ -32,8 +33,10 @@ use settings::Settings;
use std::any::TypeId; use std::any::TypeId;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use task::{DebugTaskDefinition, DebugTaskTemplate}; use task::{
use terminal_view::terminal_panel::TerminalPanel; DebugTaskDefinition, DebugTaskTemplate, HideStrategy, RevealStrategy, RevealTarget, TaskId,
};
use terminal_view::TerminalView;
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*}; use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
use workspace::{ use workspace::{
Workspace, Workspace,
@ -293,23 +296,21 @@ impl DebugPanel {
(session.clone(), dap_store.boot_session(session, cx)) (session.clone(), dap_store.boot_session(session, cx))
})?; })?;
Self::register_session(this.clone(), session.clone(), cx).await?;
match task.await { if let Err(e) = task.await {
Err(e) => { this.update(cx, |this, cx| {
this.update(cx, |this, cx| { this.workspace
this.workspace .update(cx, |workspace, cx| {
.update(cx, |workspace, cx| { workspace.show_error(&e, cx);
workspace.show_error(&e, cx); })
}) .ok();
.ok(); })
}) .ok();
.ok();
session session
.update(cx, |session, cx| session.shutdown(cx))? .update(cx, |session, cx| session.shutdown(cx))?
.await; .await;
}
Ok(_) => Self::register_session(this, session, cx).await?,
} }
anyhow::Ok(()) anyhow::Ok(())
@ -467,6 +468,7 @@ impl DebugPanel {
) { ) {
match event { match event {
dap_store::DapStoreEvent::RunInTerminal { dap_store::DapStoreEvent::RunInTerminal {
session_id,
title, title,
cwd, cwd,
command, command,
@ -476,6 +478,7 @@ impl DebugPanel {
.. ..
} => { } => {
self.handle_run_in_terminal_request( self.handle_run_in_terminal_request(
*session_id,
title.clone(), title.clone(),
cwd.clone(), cwd.clone(),
command.clone(), command.clone(),
@ -499,6 +502,7 @@ impl DebugPanel {
fn handle_run_in_terminal_request( fn handle_run_in_terminal_request(
&self, &self,
session_id: SessionId,
title: Option<String>, title: Option<String>,
cwd: Option<Arc<Path>>, cwd: Option<Arc<Path>>,
command: Option<String>, command: Option<String>,
@ -506,56 +510,83 @@ impl DebugPanel {
envs: HashMap<String, String>, envs: HashMap<String, String>,
mut sender: mpsc::Sender<Result<u32>>, mut sender: mpsc::Sender<Result<u32>>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut Context<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let terminal_task = self.workspace.update(cx, |workspace, cx| { let Some(session) = self
let terminal_panel = workspace.panel::<TerminalPanel>(cx).ok_or_else(|| { .sessions
anyhow!("RunInTerminal DAP request failed because TerminalPanel wasn't found") .iter()
}); .find(|s| s.read(cx).session_id(cx) == session_id)
else {
let terminal_panel = match terminal_panel { return Task::ready(Err(anyhow!("no session {:?} found", session_id)));
Ok(panel) => panel, };
Err(err) => return Task::ready(Err(err)), let running = session.read(cx).running_state();
}; let cwd = cwd.map(|p| p.to_path_buf());
let shell = self
terminal_panel.update(cx, |terminal_panel, cx| { .project
let terminal_task = terminal_panel.add_terminal( .read(cx)
TerminalKind::Debug { .terminal_settings(&cwd, cx)
command, .shell
args, .clone();
envs, let kind = if let Some(command) = command {
cwd, let title = title.clone().unwrap_or(command.clone());
title, TerminalKind::Task(task::SpawnInTerminal {
}, id: TaskId("debug".to_string()),
task::RevealStrategy::Never, full_label: title.clone(),
window, label: title.clone(),
cx, command: command.clone(),
); args,
command_label: title.clone(),
cx.spawn(async move |_, cx| { cwd,
let pid_task = async move { env: envs,
let terminal = terminal_task.await?; use_new_terminal: true,
allow_concurrent_runs: true,
terminal.read_with(cx, |terminal, _| terminal.pty_info.pid()) reveal: RevealStrategy::NoFocus,
}; reveal_target: RevealTarget::Dock,
hide: HideStrategy::Never,
pid_task.await shell,
}) show_summary: false,
show_command: false,
show_rerun: false,
}) })
} else {
TerminalKind::Shell(cwd.map(|c| c.to_path_buf()))
};
let workspace = self.workspace.clone();
let project = self.project.downgrade();
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?;
let terminal_view = cx.new_window_entity(|window, cx| {
TerminalView::new(terminal.clone(), workspace, None, project, window, cx)
})?;
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();
});
})?;
anyhow::Ok(terminal.read_with(cx, |terminal, _| terminal.pty_info.pid())?)
}); });
cx.background_spawn(async move { cx.background_spawn(async move {
match terminal_task { match terminal_task.await {
Ok(pid_task) => match pid_task.await { Ok(pid_task) => match pid_task {
Ok(Some(pid)) => sender.send(Ok(pid.as_u32())).await?, Some(pid) => sender.send(Ok(pid.as_u32())).await?,
Ok(None) => { None => {
sender sender
.send(Err(anyhow!( .send(Err(anyhow!(
"Terminal was spawned but PID was not available" "Terminal was spawned but PID was not available"
))) )))
.await? .await?
} }
Err(error) => sender.send(Err(anyhow!(error))).await?,
}, },
Err(error) => sender.send(Err(anyhow!(error))).await?, Err(error) => sender.send(Err(anyhow!(error))).await?,
}; };

View file

@ -9,7 +9,7 @@ use util::ResultExt;
use workspace::{Member, Pane, PaneAxis, Workspace}; use workspace::{Member, Pane, PaneAxis, Workspace};
use crate::session::running::{ 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, loaded_source_list::LoadedSourceList, module_list::ModuleList,
stack_frame_list::StackFrameList, variable_list::VariableList, stack_frame_list::StackFrameList, variable_list::VariableList,
}; };
@ -22,6 +22,7 @@ pub(crate) enum DebuggerPaneItem {
Frames, Frames,
Modules, Modules,
LoadedSources, LoadedSources,
Terminal,
} }
impl DebuggerPaneItem { impl DebuggerPaneItem {
@ -33,6 +34,7 @@ impl DebuggerPaneItem {
DebuggerPaneItem::Frames, DebuggerPaneItem::Frames,
DebuggerPaneItem::Modules, DebuggerPaneItem::Modules,
DebuggerPaneItem::LoadedSources, DebuggerPaneItem::LoadedSources,
DebuggerPaneItem::Terminal,
]; ];
VARIANTS VARIANTS
} }
@ -55,6 +57,7 @@ impl DebuggerPaneItem {
DebuggerPaneItem::Frames => SharedString::new_static("Frames"), DebuggerPaneItem::Frames => SharedString::new_static("Frames"),
DebuggerPaneItem::Modules => SharedString::new_static("Modules"), DebuggerPaneItem::Modules => SharedString::new_static("Modules"),
DebuggerPaneItem::LoadedSources => SharedString::new_static("Sources"), 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>, console: &Entity<Console>,
breakpoint_list: &Entity<BreakpointList>, breakpoint_list: &Entity<BreakpointList>,
loaded_sources: &Entity<LoadedSourceList>, loaded_sources: &Entity<LoadedSourceList>,
terminal: &Entity<DebugTerminal>,
subscriptions: &mut HashMap<EntityId, Subscription>, subscriptions: &mut HashMap<EntityId, Subscription>,
window: &mut Window, window: &mut Window,
cx: &mut Context<RunningState>, cx: &mut Context<RunningState>,
@ -191,6 +195,7 @@ pub(crate) fn deserialize_pane_layout(
console, console,
breakpoint_list, breakpoint_list,
loaded_sources, loaded_sources,
terminal,
subscriptions, subscriptions,
window, window,
cx, cx,
@ -273,6 +278,13 @@ pub(crate) fn deserialize_pane_layout(
})), })),
cx, cx,
)), )),
DebuggerPaneItem::Terminal => Box::new(SubView::new(
pane.focus_handle(cx),
terminal.clone().into(),
DebuggerPaneItem::Terminal,
None,
cx,
)),
}) })
.collect(); .collect();

View file

@ -104,6 +104,12 @@ impl DebugSession {
&self.mode &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 { pub(crate) fn label(&self, cx: &App) -> String {
if let Some(label) = self.label.get() { if let Some(label) = self.label.get() {
return label.to_owned(); return label.to_owned();

View file

@ -27,6 +27,7 @@ use project::{
use rpc::proto::ViewId; use rpc::proto::ViewId;
use settings::Settings; use settings::Settings;
use stack_frame_list::StackFrameList; use stack_frame_list::StackFrameList;
use terminal_view::TerminalView;
use ui::{ use ui::{
ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder, ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,
InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString, InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
@ -35,7 +36,7 @@ use ui::{
use util::ResultExt; use util::ResultExt;
use variable_list::VariableList; use variable_list::VariableList;
use workspace::{ use workspace::{
ActivePaneDecorator, DraggedTab, Item, Member, Pane, PaneGroup, Workspace, ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, Workspace,
item::TabContentParams, move_item, pane::Event, item::TabContentParams, move_item, pane::Event,
}; };
@ -50,6 +51,7 @@ pub struct RunningState {
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
stack_frame_list: Entity<stack_frame_list::StackFrameList>, stack_frame_list: Entity<stack_frame_list::StackFrameList>,
loaded_sources_list: Entity<LoadedSourceList>, loaded_sources_list: Entity<LoadedSourceList>,
pub debug_terminal: Entity<DebugTerminal>,
module_list: Entity<module_list::ModuleList>, module_list: Entity<module_list::ModuleList>,
_console: Entity<Console>, _console: Entity<Console>,
breakpoint_list: Entity<BreakpointList>, breakpoint_list: Entity<BreakpointList>,
@ -364,6 +366,40 @@ pub(crate) fn new_debugger_pane(
ret 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 { impl RunningState {
pub fn new( pub fn new(
session: Entity<Session>, session: Entity<Session>,
@ -380,6 +416,8 @@ impl RunningState {
StackFrameList::new(workspace.clone(), session.clone(), weak_state, window, cx) StackFrameList::new(workspace.clone(), session.clone(), weak_state, window, cx)
}); });
let debug_terminal = cx.new(DebugTerminal::empty);
let variable_list = let variable_list =
cx.new(|cx| VariableList::new(session.clone(), stack_frame_list.clone(), window, cx)); cx.new(|cx| VariableList::new(session.clone(), stack_frame_list.clone(), window, cx));
@ -452,6 +490,7 @@ impl RunningState {
&console, &console,
&breakpoint_list, &breakpoint_list,
&loaded_source_list, &loaded_source_list,
&debug_terminal,
&mut pane_close_subscriptions, &mut pane_close_subscriptions,
window, window,
cx, cx,
@ -494,6 +533,7 @@ impl RunningState {
breakpoint_list, breakpoint_list,
loaded_sources_list: loaded_source_list, loaded_sources_list: loaded_source_list,
pane_close_subscriptions, pane_close_subscriptions,
debug_terminal,
_schedule_serialize: None, _schedule_serialize: None,
} }
} }
@ -525,6 +565,90 @@ impl RunningState {
self.panes.pane_at_pixel_position(position).is_some() 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( pub(crate) fn add_pane_item(
&mut self, &mut self,
item_kind: DebuggerPaneItem, item_kind: DebuggerPaneItem,
@ -538,58 +662,7 @@ impl RunningState {
); );
if let Some(pane) = self.panes.pane_at_pixel_position(position) { if let Some(pane) = self.panes.pane_at_pixel_position(position) {
let sub_view = match item_kind { let sub_view = self.create_sub_view(item_kind, pane, cx);
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,
)),
};
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
pane.add_item(sub_view, false, false, None, window, cx); pane.add_item(sub_view, false, false, None, window, cx);

View file

@ -1,4 +1,4 @@
use crate::{tests::start_debug_session, *}; use crate::{persistence::DebuggerPaneItem, tests::start_debug_session, *};
use dap::{ use dap::{
ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint, ErrorResponse, Message, RunInTerminalRequestArguments, SourceBreakpoint,
StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest, StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
@ -25,7 +25,7 @@ use std::{
atomic::{AtomicBool, Ordering}, 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 tests::{active_debug_session_panel, init_test, init_test_workspace};
use util::path; use util::path;
use workspace::{Item, dock::Panel}; use workspace::{Item, dock::Panel};
@ -385,22 +385,17 @@ async fn test_handle_successful_run_in_terminal_reverse_request(
workspace workspace
.update(cx, |workspace, _window, cx| { .update(cx, |workspace, _window, cx| {
let terminal_panel = workspace.panel::<TerminalPanel>(cx).unwrap(); let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
let session = debug_panel.read(cx).active_session().unwrap();
let panel = terminal_panel.read(cx).pane().unwrap().read(cx); let running = session.read(cx).running_state();
assert_eq!(
assert_eq!(1, panel.items_len()); running
assert!(
panel
.active_item()
.unwrap()
.downcast::<TerminalView>()
.unwrap()
.read(cx) .read(cx)
.terminal() .pane_items_status(cx)
.read(cx) .get(&DebuggerPaneItem::Terminal),
.debug_terminal() Some(&true)
); );
assert!(running.read(cx).debug_terminal.read(cx).terminal.is_some());
}) })
.unwrap(); .unwrap();

View file

@ -31,14 +31,6 @@ pub enum TerminalKind {
Shell(Option<PathBuf>), Shell(Option<PathBuf>),
/// Run a task. /// Run a task.
Task(SpawnInTerminal), 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 /// SshCommand describes how to connect to a remote server
@ -105,7 +97,6 @@ impl Project {
self.active_project_directory(cx) self.active_project_directory(cx)
} }
} }
TerminalKind::Debug { cwd, .. } => cwd.clone(),
}; };
let mut settings_location = None; let mut settings_location = None;
@ -209,7 +200,6 @@ impl Project {
this.active_project_directory(cx) this.active_project_directory(cx)
} }
} }
TerminalKind::Debug { cwd, .. } => cwd.clone(),
}; };
let ssh_details = this.ssh_details(cx); let ssh_details = this.ssh_details(cx);
@ -243,7 +233,6 @@ impl Project {
}; };
let mut python_venv_activate_command = None; let mut python_venv_activate_command = None;
let debug_terminal = matches!(kind, TerminalKind::Debug { .. });
let (spawn_task, shell) = match kind { let (spawn_task, shell) = match kind {
TerminalKind::Shell(_) => { 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( TerminalBuilder::new(
local_path.map(|path| path.to_path_buf()), local_path.map(|path| path.to_path_buf()),
@ -373,7 +341,6 @@ impl Project {
ssh_details.is_some(), ssh_details.is_some(),
window, window,
completion_tx, completion_tx,
debug_terminal,
cx, cx,
) )
.map(|builder| { .map(|builder| {

View file

@ -352,7 +352,6 @@ impl TerminalBuilder {
is_ssh_terminal: bool, is_ssh_terminal: bool,
window: AnyWindowHandle, window: AnyWindowHandle,
completion_tx: Sender<Option<ExitStatus>>, completion_tx: Sender<Option<ExitStatus>>,
debug_terminal: bool,
cx: &App, cx: &App,
) -> Result<TerminalBuilder> { ) -> Result<TerminalBuilder> {
// If the parent environment doesn't have a locale set // If the parent environment doesn't have a locale set
@ -502,7 +501,6 @@ impl TerminalBuilder {
word_regex: RegexSearch::new(WORD_REGEX).unwrap(), word_regex: RegexSearch::new(WORD_REGEX).unwrap(),
python_file_line_regex: RegexSearch::new(PYTHON_FILE_LINE_REGEX).unwrap(), python_file_line_regex: RegexSearch::new(PYTHON_FILE_LINE_REGEX).unwrap(),
vi_mode_enabled: false, vi_mode_enabled: false,
debug_terminal,
is_ssh_terminal, is_ssh_terminal,
python_venv_directory, python_venv_directory,
}; };
@ -660,7 +658,6 @@ pub struct Terminal {
python_file_line_regex: RegexSearch, python_file_line_regex: RegexSearch,
task: Option<TaskState>, task: Option<TaskState>,
vi_mode_enabled: bool, vi_mode_enabled: bool,
debug_terminal: bool,
is_ssh_terminal: bool, is_ssh_terminal: bool,
} }
@ -1855,10 +1852,6 @@ impl Terminal {
self.task.as_ref() 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>> { pub fn wait_for_completed_task(&self, cx: &App) -> Task<Option<ExitStatus>> {
if let Some(task) = self.task() { if let Some(task) = self.task() {
if task.status == TaskStatus::Running { if task.status == TaskStatus::Running {

View file

@ -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), None => (IconName::Terminal, Color::Muted, None),
}; };
@ -1583,7 +1580,7 @@ impl SerializableItem for TerminalView {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Option<Task<gpui::Result<()>>> { ) -> Option<Task<gpui::Result<()>>> {
let terminal = self.terminal().read(cx); let terminal = self.terminal().read(cx);
if terminal.task().is_some() || terminal.debug_terminal() { if terminal.task().is_some() {
return None; return None;
} }

View file

@ -142,6 +142,10 @@ impl PaneGroup {
self.root.first_pane() self.root.first_pane()
} }
pub fn last_pane(&self) -> Entity<Pane> {
self.root.last_pane()
}
pub fn find_pane_in_direction( pub fn find_pane_in_direction(
&mut self, &mut self,
active_pane: &Entity<Pane>, 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( pub fn render(
&self, &self,
basis: usize, basis: usize,