debugger: Remember pane layout from previous debugger session (#28692)
This PR makes a debugger's pane layout persistent across session's that use the same debug adapter. Release Notes: - N/A --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
parent
b794919842
commit
d4761cea47
13 changed files with 550 additions and 150 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4177,6 +4177,7 @@ dependencies = [
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette_hooks",
|
"command_palette_hooks",
|
||||||
"dap",
|
"dap",
|
||||||
|
"db",
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use sqlez_macros::sql;
|
use sqlez_macros::sql;
|
||||||
|
|
||||||
use crate::{define_connection, query};
|
use crate::{define_connection, query};
|
||||||
|
pub static DEBUGGER_PANEL_PREFIX: &str = "debugger_panel_";
|
||||||
|
|
||||||
define_connection!(pub static ref KEY_VALUE_STORE: KeyValueStore<()> =
|
define_connection!(pub static ref KEY_VALUE_STORE: KeyValueStore<()> =
|
||||||
&[sql!(
|
&[sql!(
|
||||||
|
|
|
@ -28,6 +28,7 @@ client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
command_palette_hooks.workspace = true
|
||||||
dap.workspace = true
|
dap.workspace = true
|
||||||
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack,
|
ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack,
|
||||||
StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints,
|
StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
|
||||||
};
|
};
|
||||||
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
|
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
|
@ -293,35 +293,49 @@ impl DebugPanel {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(project) = self.project.upgrade() else {
|
let adapter_name = session.read(cx).adapter_name();
|
||||||
return log::error!("Debug Panel out lived it's weak reference to Project");
|
|
||||||
};
|
|
||||||
|
|
||||||
if self
|
let session_id = *session_id;
|
||||||
.sessions
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
.iter()
|
let serialized_layout =
|
||||||
.any(|item| item.read(cx).session_id(cx) == *session_id)
|
persistence::get_serialized_pane_layout(adapter_name).await;
|
||||||
{
|
|
||||||
// We already have an item for this session.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let session_item = DebugSession::running(
|
|
||||||
project,
|
|
||||||
self.workspace.clone(),
|
|
||||||
session,
|
|
||||||
cx.weak_entity(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(running) = session_item.read(cx).mode().as_running().cloned() {
|
this.update_in(cx, |this, window, cx| {
|
||||||
// We might want to make this an event subscription and only notify when a new thread is selected
|
let Some(project) = this.project.upgrade() else {
|
||||||
// This is used to filter the command menu correctly
|
return log::error!(
|
||||||
cx.observe(&running, |_, _, cx| cx.notify()).detach();
|
"Debug Panel out lived it's weak reference to Project"
|
||||||
}
|
);
|
||||||
|
};
|
||||||
|
|
||||||
self.sessions.push(session_item.clone());
|
if this
|
||||||
self.activate_session(session_item, window, cx);
|
.sessions
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.read(cx).session_id(cx) == session_id)
|
||||||
|
{
|
||||||
|
// We already have an item for this session.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let session_item = DebugSession::running(
|
||||||
|
project,
|
||||||
|
this.workspace.clone(),
|
||||||
|
session,
|
||||||
|
cx.weak_entity(),
|
||||||
|
serialized_layout,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(running) = session_item.read(cx).mode().as_running().cloned() {
|
||||||
|
// We might want to make this an event subscription and only notify when a new thread is selected
|
||||||
|
// This is used to filter the command menu correctly
|
||||||
|
cx.observe(&running, |_, _, cx| cx.notify()).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessions.push(session_item.clone());
|
||||||
|
this.activate_session(session_item, window, cx);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
dap_store::DapStoreEvent::RunInTerminal {
|
dap_store::DapStoreEvent::RunInTerminal {
|
||||||
title,
|
title,
|
||||||
|
|
|
@ -13,6 +13,7 @@ use workspace::{ShutdownDebugAdapters, Workspace};
|
||||||
pub mod attach_modal;
|
pub mod attach_modal;
|
||||||
pub mod debugger_panel;
|
pub mod debugger_panel;
|
||||||
mod new_session_modal;
|
mod new_session_modal;
|
||||||
|
mod persistence;
|
||||||
pub(crate) mod session;
|
pub(crate) mod session;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
259
crates/debugger_ui/src/persistence.rs
Normal file
259
crates/debugger_ui/src/persistence.rs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
use collections::HashMap;
|
||||||
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
|
use gpui::{Axis, Context, Entity, EntityId, Focusable, Subscription, WeakEntity, Window};
|
||||||
|
use project::Project;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ui::{App, SharedString};
|
||||||
|
use util::ResultExt;
|
||||||
|
use workspace::{Member, Pane, PaneAxis, Workspace};
|
||||||
|
|
||||||
|
use crate::session::running::{
|
||||||
|
self, RunningState, SubView, breakpoint_list::BreakpointList, console::Console,
|
||||||
|
module_list::ModuleList, stack_frame_list::StackFrameList, variable_list::VariableList,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub(crate) enum DebuggerPaneItem {
|
||||||
|
Console,
|
||||||
|
Variables,
|
||||||
|
BreakpointList,
|
||||||
|
Frames,
|
||||||
|
Modules,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebuggerPaneItem {
|
||||||
|
pub(crate) fn to_shared_string(self) -> SharedString {
|
||||||
|
match self {
|
||||||
|
DebuggerPaneItem::Console => SharedString::new_static("Console"),
|
||||||
|
DebuggerPaneItem::Variables => SharedString::new_static("Variables"),
|
||||||
|
DebuggerPaneItem::BreakpointList => SharedString::new_static("Breakpoints"),
|
||||||
|
DebuggerPaneItem::Frames => SharedString::new_static("Frames"),
|
||||||
|
DebuggerPaneItem::Modules => SharedString::new_static("Modules"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct SerializedAxis(pub Axis);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) enum SerializedPaneLayout {
|
||||||
|
Pane(SerializedPane),
|
||||||
|
Group {
|
||||||
|
axis: SerializedAxis,
|
||||||
|
flexes: Option<Vec<f32>>,
|
||||||
|
children: Vec<SerializedPaneLayout>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct SerializedPane {
|
||||||
|
pub children: Vec<DebuggerPaneItem>,
|
||||||
|
pub active_item: Option<DebuggerPaneItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn serialize_pane_layout(
|
||||||
|
adapter_name: SharedString,
|
||||||
|
pane_group: SerializedPaneLayout,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if let Ok(serialized_pane_group) = serde_json::to_string(&pane_group) {
|
||||||
|
KEY_VALUE_STORE
|
||||||
|
.write_kvp(
|
||||||
|
format!("{}-{adapter_name}", db::kvp::DEBUGGER_PANEL_PREFIX),
|
||||||
|
serialized_pane_group,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Failed to serialize pane group with serde_json as a string"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_serialized_pane_layout(
|
||||||
|
pane_group: &Member,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> SerializedPaneLayout {
|
||||||
|
match pane_group {
|
||||||
|
Member::Axis(PaneAxis {
|
||||||
|
axis,
|
||||||
|
members,
|
||||||
|
flexes,
|
||||||
|
bounding_boxes: _,
|
||||||
|
}) => SerializedPaneLayout::Group {
|
||||||
|
axis: SerializedAxis(*axis),
|
||||||
|
children: members
|
||||||
|
.iter()
|
||||||
|
.map(|member| build_serialized_pane_layout(member, cx))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
flexes: Some(flexes.lock().clone()),
|
||||||
|
},
|
||||||
|
Member::Pane(pane_handle) => SerializedPaneLayout::Pane(serialize_pane(pane_handle, cx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_pane(pane: &Entity<Pane>, cx: &mut App) -> SerializedPane {
|
||||||
|
let pane = pane.read(cx);
|
||||||
|
let children = pane
|
||||||
|
.items()
|
||||||
|
.filter_map(|item| {
|
||||||
|
item.act_as::<SubView>(cx)
|
||||||
|
.map(|view| view.read(cx).view_kind())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let active_item = pane
|
||||||
|
.active_item()
|
||||||
|
.and_then(|item| item.act_as::<SubView>(cx))
|
||||||
|
.map(|view| view.read(cx).view_kind());
|
||||||
|
|
||||||
|
SerializedPane {
|
||||||
|
children,
|
||||||
|
active_item,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_serialized_pane_layout(
|
||||||
|
adapter_name: impl AsRef<str>,
|
||||||
|
) -> Option<SerializedPaneLayout> {
|
||||||
|
let key = format!(
|
||||||
|
"{}-{}",
|
||||||
|
db::kvp::DEBUGGER_PANEL_PREFIX,
|
||||||
|
adapter_name.as_ref()
|
||||||
|
);
|
||||||
|
|
||||||
|
KEY_VALUE_STORE
|
||||||
|
.read_kvp(&key)
|
||||||
|
.log_err()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|value| serde_json::from_str::<SerializedPaneLayout>(&value).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_pane_layout(
|
||||||
|
serialized: SerializedPaneLayout,
|
||||||
|
workspace: &WeakEntity<Workspace>,
|
||||||
|
project: &Entity<Project>,
|
||||||
|
stack_frame_list: &Entity<StackFrameList>,
|
||||||
|
variable_list: &Entity<VariableList>,
|
||||||
|
module_list: &Entity<ModuleList>,
|
||||||
|
console: &Entity<Console>,
|
||||||
|
breakpoint_list: &Entity<BreakpointList>,
|
||||||
|
subscriptions: &mut HashMap<EntityId, Subscription>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<RunningState>,
|
||||||
|
) -> Option<Member> {
|
||||||
|
match serialized {
|
||||||
|
SerializedPaneLayout::Group {
|
||||||
|
axis,
|
||||||
|
flexes,
|
||||||
|
children,
|
||||||
|
} => {
|
||||||
|
let mut members = Vec::new();
|
||||||
|
for child in children {
|
||||||
|
if let Some(new_member) = deserialize_pane_layout(
|
||||||
|
child,
|
||||||
|
workspace,
|
||||||
|
project,
|
||||||
|
stack_frame_list,
|
||||||
|
variable_list,
|
||||||
|
module_list,
|
||||||
|
console,
|
||||||
|
breakpoint_list,
|
||||||
|
subscriptions,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
) {
|
||||||
|
members.push(new_member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if members.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if members.len() == 1 {
|
||||||
|
return Some(members.remove(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Member::Axis(PaneAxis::load(
|
||||||
|
axis.0,
|
||||||
|
members,
|
||||||
|
flexes.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
SerializedPaneLayout::Pane(serialized_pane) => {
|
||||||
|
let pane = running::new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||||
|
subscriptions.insert(
|
||||||
|
pane.entity_id(),
|
||||||
|
cx.subscribe_in(&pane, window, RunningState::handle_pane_event),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sub_views: Vec<_> = serialized_pane
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| match child {
|
||||||
|
DebuggerPaneItem::Frames => Box::new(SubView::new(
|
||||||
|
pane.focus_handle(cx),
|
||||||
|
stack_frame_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Frames,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
DebuggerPaneItem::Variables => Box::new(SubView::new(
|
||||||
|
variable_list.focus_handle(cx),
|
||||||
|
variable_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Variables,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
DebuggerPaneItem::BreakpointList => Box::new(SubView::new(
|
||||||
|
breakpoint_list.focus_handle(cx),
|
||||||
|
breakpoint_list.clone().into(),
|
||||||
|
DebuggerPaneItem::BreakpointList,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
DebuggerPaneItem::Modules => Box::new(SubView::new(
|
||||||
|
pane.focus_handle(cx),
|
||||||
|
module_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Modules,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
|
||||||
|
DebuggerPaneItem::Console => Box::new(SubView::new(
|
||||||
|
pane.focus_handle(cx),
|
||||||
|
console.clone().into(),
|
||||||
|
DebuggerPaneItem::Console,
|
||||||
|
Some(Box::new({
|
||||||
|
let console = console.clone().downgrade();
|
||||||
|
move |cx| {
|
||||||
|
console
|
||||||
|
.read_with(cx, |console, cx| console.show_indicator(cx))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
pane.update(cx, |pane, cx| {
|
||||||
|
let mut active_idx = 0;
|
||||||
|
for (idx, sub_view) in sub_views.into_iter().enumerate() {
|
||||||
|
if serialized_pane
|
||||||
|
.active_item
|
||||||
|
.is_some_and(|active| active == sub_view.read(cx).view_kind())
|
||||||
|
{
|
||||||
|
active_idx = idx;
|
||||||
|
}
|
||||||
|
pane.add_item(sub_view, false, false, None, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pane.activate_item(active_idx, false, false, window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(Member::Pane(pane.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ use workspace::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::debugger_panel::DebugPanel;
|
use crate::debugger_panel::DebugPanel;
|
||||||
|
use crate::persistence::SerializedPaneLayout;
|
||||||
|
|
||||||
pub(crate) enum DebugSessionState {
|
pub(crate) enum DebugSessionState {
|
||||||
Running(Entity<running::RunningState>),
|
Running(Entity<running::RunningState>),
|
||||||
|
@ -52,6 +53,7 @@ impl DebugSession {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
session: Entity<Session>,
|
session: Entity<Session>,
|
||||||
_debug_panel: WeakEntity<DebugPanel>,
|
_debug_panel: WeakEntity<DebugPanel>,
|
||||||
|
serialized_pane_layout: Option<SerializedPaneLayout>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Entity<Self> {
|
) -> Entity<Self> {
|
||||||
|
@ -60,6 +62,7 @@ impl DebugSession {
|
||||||
session.clone(),
|
session.clone(),
|
||||||
project.clone(),
|
project.clone(),
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
|
serialized_pane_layout,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
mod breakpoint_list;
|
pub(crate) mod breakpoint_list;
|
||||||
mod console;
|
pub(crate) mod console;
|
||||||
mod loaded_source_list;
|
pub(crate) mod loaded_source_list;
|
||||||
mod module_list;
|
pub(crate) mod module_list;
|
||||||
pub mod stack_frame_list;
|
pub mod stack_frame_list;
|
||||||
pub mod variable_list;
|
pub mod variable_list;
|
||||||
|
|
||||||
use std::{any::Any, ops::ControlFlow, sync::Arc};
|
use std::{any::Any, ops::ControlFlow, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use crate::persistence::{self, DebuggerPaneItem, SerializedPaneLayout};
|
||||||
|
|
||||||
use super::DebugPanelItemEvent;
|
use super::DebugPanelItemEvent;
|
||||||
use breakpoint_list::BreakpointList;
|
use breakpoint_list::BreakpointList;
|
||||||
|
@ -14,7 +16,7 @@ use console::Console;
|
||||||
use dap::{Capabilities, Thread, client::SessionId, debugger_settings::DebuggerSettings};
|
use dap::{Capabilities, Thread, client::SessionId, debugger_settings::DebuggerSettings};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
|
Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||||
NoAction, Subscription, WeakEntity,
|
NoAction, Subscription, Task, WeakEntity,
|
||||||
};
|
};
|
||||||
use loaded_source_list::LoadedSourceList;
|
use loaded_source_list::LoadedSourceList;
|
||||||
use module_list::ModuleList;
|
use module_list::ModuleList;
|
||||||
|
@ -33,8 +35,8 @@ use ui::{
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use variable_list::VariableList;
|
use variable_list::VariableList;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
ActivePaneDecorator, DraggedTab, Item, Pane, PaneGroup, Workspace, item::TabContentParams,
|
ActivePaneDecorator, DraggedTab, Item, Member, Pane, PaneGroup, Workspace,
|
||||||
move_item, pane::Event,
|
item::TabContentParams, move_item, pane::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RunningState {
|
pub struct RunningState {
|
||||||
|
@ -51,6 +53,7 @@ pub struct RunningState {
|
||||||
_console: Entity<Console>,
|
_console: Entity<Console>,
|
||||||
panes: PaneGroup,
|
panes: PaneGroup,
|
||||||
pane_close_subscriptions: HashMap<EntityId, Subscription>,
|
pane_close_subscriptions: HashMap<EntityId, Subscription>,
|
||||||
|
_schedule_serialize: Option<Task<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for RunningState {
|
impl Render for RunningState {
|
||||||
|
@ -84,28 +87,32 @@ impl Render for RunningState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SubView {
|
pub(crate) struct SubView {
|
||||||
inner: AnyView,
|
inner: AnyView,
|
||||||
pane_focus_handle: FocusHandle,
|
pane_focus_handle: FocusHandle,
|
||||||
tab_name: SharedString,
|
kind: DebuggerPaneItem,
|
||||||
show_indicator: Box<dyn Fn(&App) -> bool>,
|
show_indicator: Box<dyn Fn(&App) -> bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubView {
|
impl SubView {
|
||||||
fn new(
|
pub(crate) fn new(
|
||||||
pane_focus_handle: FocusHandle,
|
pane_focus_handle: FocusHandle,
|
||||||
view: AnyView,
|
view: AnyView,
|
||||||
tab_name: SharedString,
|
kind: DebuggerPaneItem,
|
||||||
show_indicator: Option<Box<dyn Fn(&App) -> bool>>,
|
show_indicator: Option<Box<dyn Fn(&App) -> bool>>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Entity<Self> {
|
) -> Entity<Self> {
|
||||||
cx.new(|_| Self {
|
cx.new(|_| Self {
|
||||||
tab_name,
|
kind,
|
||||||
inner: view,
|
inner: view,
|
||||||
pane_focus_handle,
|
pane_focus_handle,
|
||||||
show_indicator: show_indicator.unwrap_or(Box::new(|_| false)),
|
show_indicator: show_indicator.unwrap_or(Box::new(|_| false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn view_kind(&self) -> DebuggerPaneItem {
|
||||||
|
self.kind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Focusable for SubView {
|
impl Focusable for SubView {
|
||||||
fn focus_handle(&self, _: &App) -> FocusHandle {
|
fn focus_handle(&self, _: &App) -> FocusHandle {
|
||||||
|
@ -116,13 +123,19 @@ impl EventEmitter<()> for SubView {}
|
||||||
impl Item for SubView {
|
impl Item for SubView {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
|
|
||||||
|
/// This is used to serialize debugger pane layouts
|
||||||
|
/// A SharedString gets converted to a enum and back during serialization/deserialization.
|
||||||
|
fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
|
||||||
|
Some(self.kind.to_shared_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn tab_content(
|
fn tab_content(
|
||||||
&self,
|
&self,
|
||||||
params: workspace::item::TabContentParams,
|
params: workspace::item::TabContentParams,
|
||||||
_: &Window,
|
_: &Window,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let label = Label::new(self.tab_name.clone())
|
let label = Label::new(self.kind.to_shared_string())
|
||||||
.size(ui::LabelSize::Small)
|
.size(ui::LabelSize::Small)
|
||||||
.color(params.text_color())
|
.color(params.text_color())
|
||||||
.line_height_style(ui::LineHeightStyle::UiLabel);
|
.line_height_style(ui::LineHeightStyle::UiLabel);
|
||||||
|
@ -146,7 +159,7 @@ impl Render for SubView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_debugger_pane(
|
pub(crate) fn new_debugger_pane(
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -185,7 +198,7 @@ fn new_debugger_pane(
|
||||||
new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||||
let _previous_subscription = running.pane_close_subscriptions.insert(
|
let _previous_subscription = running.pane_close_subscriptions.insert(
|
||||||
new_pane.entity_id(),
|
new_pane.entity_id(),
|
||||||
cx.subscribe(&new_pane, RunningState::handle_pane_event),
|
cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event),
|
||||||
);
|
);
|
||||||
debug_assert!(_previous_subscription.is_none());
|
debug_assert!(_previous_subscription.is_none());
|
||||||
running
|
running
|
||||||
|
@ -354,6 +367,7 @@ impl RunningState {
|
||||||
session: Entity<Session>,
|
session: Entity<Session>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
|
serialized_pane_layout: Option<SerializedPaneLayout>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -382,6 +396,8 @@ impl RunningState {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let breakpoints = BreakpointList::new(session.clone(), workspace.clone(), &project, cx);
|
||||||
|
|
||||||
let _subscriptions = vec![
|
let _subscriptions = vec![
|
||||||
cx.observe(&module_list, |_, _, cx| cx.notify()),
|
cx.observe(&module_list, |_, _, cx| cx.notify()),
|
||||||
cx.subscribe_in(&session, window, |this, _, event, window, cx| {
|
cx.subscribe_in(&session, window, |this, _, event, window, cx| {
|
||||||
|
@ -407,112 +423,40 @@ impl RunningState {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let leftmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
let mut pane_close_subscriptions = HashMap::default();
|
||||||
leftmost_pane.update(cx, |this, cx| {
|
let panes = if let Some(root) = serialized_pane_layout.and_then(|serialized_layout| {
|
||||||
this.add_item(
|
persistence::deserialize_pane_layout(
|
||||||
Box::new(SubView::new(
|
serialized_layout,
|
||||||
this.focus_handle(cx),
|
&workspace,
|
||||||
stack_frame_list.clone().into(),
|
&project,
|
||||||
SharedString::new_static("Frames"),
|
&stack_frame_list,
|
||||||
None,
|
&variable_list,
|
||||||
cx,
|
&module_list,
|
||||||
)),
|
&console,
|
||||||
true,
|
&breakpoints,
|
||||||
false,
|
&mut pane_close_subscriptions,
|
||||||
None,
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
workspace::PaneGroup::with_root(root)
|
||||||
|
} else {
|
||||||
|
pane_close_subscriptions.clear();
|
||||||
|
let root = Self::default_pane_layout(
|
||||||
|
project,
|
||||||
|
&workspace,
|
||||||
|
&stack_frame_list,
|
||||||
|
&variable_list,
|
||||||
|
&module_list,
|
||||||
|
&console,
|
||||||
|
breakpoints,
|
||||||
|
&mut pane_close_subscriptions,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
let breakpoints = BreakpointList::new(session.clone(), workspace.clone(), &project, cx);
|
|
||||||
this.add_item(
|
|
||||||
Box::new(SubView::new(
|
|
||||||
breakpoints.focus_handle(cx),
|
|
||||||
breakpoints.into(),
|
|
||||||
SharedString::new_static("Breakpoints"),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)),
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
this.activate_item(0, false, false, window, cx);
|
|
||||||
});
|
|
||||||
let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
|
||||||
center_pane.update(cx, |this, cx| {
|
|
||||||
this.add_item(
|
|
||||||
Box::new(SubView::new(
|
|
||||||
variable_list.focus_handle(cx),
|
|
||||||
variable_list.clone().into(),
|
|
||||||
SharedString::new_static("Variables"),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)),
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
this.add_item(
|
|
||||||
Box::new(SubView::new(
|
|
||||||
this.focus_handle(cx),
|
|
||||||
module_list.clone().into(),
|
|
||||||
SharedString::new_static("Modules"),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)),
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
this.activate_item(0, false, false, window, cx);
|
|
||||||
});
|
|
||||||
let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
|
||||||
rightmost_pane.update(cx, |this, cx| {
|
|
||||||
let weak_console = console.downgrade();
|
|
||||||
this.add_item(
|
|
||||||
Box::new(SubView::new(
|
|
||||||
this.focus_handle(cx),
|
|
||||||
console.clone().into(),
|
|
||||||
SharedString::new_static("Console"),
|
|
||||||
Some(Box::new(move |cx| {
|
|
||||||
weak_console
|
|
||||||
.read_with(cx, |console, cx| console.show_indicator(cx))
|
|
||||||
.unwrap_or_default()
|
|
||||||
})),
|
|
||||||
cx,
|
|
||||||
)),
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
let pane_close_subscriptions = HashMap::from_iter(
|
|
||||||
[&leftmost_pane, ¢er_pane, &rightmost_pane]
|
|
||||||
.into_iter()
|
|
||||||
.map(|entity| {
|
|
||||||
(
|
|
||||||
entity.entity_id(),
|
|
||||||
cx.subscribe(entity, Self::handle_pane_event),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let group_root = workspace::PaneAxis::new(
|
|
||||||
gpui::Axis::Horizontal,
|
|
||||||
[leftmost_pane, center_pane, rightmost_pane]
|
|
||||||
.into_iter()
|
|
||||||
.map(workspace::Member::Pane)
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let panes = PaneGroup::with_root(workspace::Member::Axis(group_root));
|
workspace::PaneGroup::with_root(root)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
session,
|
session,
|
||||||
|
@ -528,21 +472,57 @@ impl RunningState {
|
||||||
_module_list: module_list,
|
_module_list: module_list,
|
||||||
_console: console,
|
_console: console,
|
||||||
pane_close_subscriptions,
|
pane_close_subscriptions,
|
||||||
|
_schedule_serialize: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_pane_event(
|
fn serialize_layout(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
if self._schedule_serialize.is_none() {
|
||||||
|
self._schedule_serialize = Some(cx.spawn_in(window, async move |this, cx| {
|
||||||
|
cx.background_executor()
|
||||||
|
.timer(Duration::from_millis(100))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let Some((adapter_name, pane_group)) = this
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
let adapter_name = this.session.read(cx).adapter_name();
|
||||||
|
(
|
||||||
|
adapter_name,
|
||||||
|
persistence::build_serialized_pane_layout(&this.panes.root, cx),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
persistence::serialize_pane_layout(adapter_name, pane_group)
|
||||||
|
.await
|
||||||
|
.log_err();
|
||||||
|
|
||||||
|
this.update(cx, |this, _| {
|
||||||
|
this._schedule_serialize.take();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn handle_pane_event(
|
||||||
this: &mut RunningState,
|
this: &mut RunningState,
|
||||||
source_pane: Entity<Pane>,
|
source_pane: &Entity<Pane>,
|
||||||
event: &Event,
|
event: &Event,
|
||||||
|
window: &mut Window,
|
||||||
cx: &mut Context<RunningState>,
|
cx: &mut Context<RunningState>,
|
||||||
) {
|
) {
|
||||||
|
this.serialize_layout(window, cx);
|
||||||
if let Event::Remove { .. } = event {
|
if let Event::Remove { .. } = event {
|
||||||
let _did_find_pane = this.panes.remove(&source_pane).is_ok();
|
let _did_find_pane = this.panes.remove(&source_pane).is_ok();
|
||||||
debug_assert!(_did_find_pane);
|
debug_assert!(_did_find_pane);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn go_to_selected_stack_frame(&self, window: &Window, cx: &mut Context<Self>) {
|
pub(crate) fn go_to_selected_stack_frame(&self, window: &Window, cx: &mut Context<Self>) {
|
||||||
if self.thread_id.is_some() {
|
if self.thread_id.is_some() {
|
||||||
self.stack_frame_list
|
self.stack_frame_list
|
||||||
|
@ -586,7 +566,7 @@ impl RunningState {
|
||||||
.find_map(|pane| {
|
.find_map(|pane| {
|
||||||
pane.read(cx)
|
pane.read(cx)
|
||||||
.items_of_type::<SubView>()
|
.items_of_type::<SubView>()
|
||||||
.position(|view| view.read(cx).tab_name == *"Modules")
|
.position(|view| view.read(cx).view_kind().to_shared_string() == *"Modules")
|
||||||
.map(|view| (view, pane))
|
.map(|view| (view, pane))
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -802,6 +782,127 @@ impl RunningState {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_pane_layout(
|
||||||
|
project: Entity<Project>,
|
||||||
|
workspace: &WeakEntity<Workspace>,
|
||||||
|
stack_frame_list: &Entity<StackFrameList>,
|
||||||
|
variable_list: &Entity<VariableList>,
|
||||||
|
module_list: &Entity<ModuleList>,
|
||||||
|
console: &Entity<Console>,
|
||||||
|
breakpoints: Entity<BreakpointList>,
|
||||||
|
subscriptions: &mut HashMap<EntityId, Subscription>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<'_, RunningState>,
|
||||||
|
) -> Member {
|
||||||
|
let leftmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||||
|
leftmost_pane.update(cx, |this, cx| {
|
||||||
|
this.add_item(
|
||||||
|
Box::new(SubView::new(
|
||||||
|
this.focus_handle(cx),
|
||||||
|
stack_frame_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Frames,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
this.add_item(
|
||||||
|
Box::new(SubView::new(
|
||||||
|
breakpoints.focus_handle(cx),
|
||||||
|
breakpoints.into(),
|
||||||
|
DebuggerPaneItem::BreakpointList,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
this.activate_item(0, false, false, window, cx);
|
||||||
|
});
|
||||||
|
let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||||
|
center_pane.update(cx, |this, cx| {
|
||||||
|
this.add_item(
|
||||||
|
Box::new(SubView::new(
|
||||||
|
variable_list.focus_handle(cx),
|
||||||
|
variable_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Variables,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
this.add_item(
|
||||||
|
Box::new(SubView::new(
|
||||||
|
this.focus_handle(cx),
|
||||||
|
module_list.clone().into(),
|
||||||
|
DebuggerPaneItem::Modules,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
this.activate_item(0, false, false, window, cx);
|
||||||
|
});
|
||||||
|
let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
|
||||||
|
rightmost_pane.update(cx, |this, cx| {
|
||||||
|
let weak_console = console.downgrade();
|
||||||
|
this.add_item(
|
||||||
|
Box::new(SubView::new(
|
||||||
|
this.focus_handle(cx),
|
||||||
|
console.clone().into(),
|
||||||
|
DebuggerPaneItem::Console,
|
||||||
|
Some(Box::new(move |cx| {
|
||||||
|
weak_console
|
||||||
|
.read_with(cx, |console, cx| console.show_indicator(cx))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})),
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
subscriptions.extend(
|
||||||
|
[&leftmost_pane, ¢er_pane, &rightmost_pane]
|
||||||
|
.into_iter()
|
||||||
|
.map(|entity| {
|
||||||
|
(
|
||||||
|
entity.entity_id(),
|
||||||
|
cx.subscribe_in(entity, window, Self::handle_pane_event),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let group_root = workspace::PaneAxis::new(
|
||||||
|
gpui::Axis::Horizontal,
|
||||||
|
[leftmost_pane, center_pane, rightmost_pane]
|
||||||
|
.into_iter()
|
||||||
|
.map(workspace::Member::Pane)
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Member::Axis(group_root)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<DebugPanelItemEvent> for RunningState {}
|
impl EventEmitter<DebugPanelItemEvent> for RunningState {}
|
||||||
|
|
|
@ -27,7 +27,7 @@ use ui::{
|
||||||
use util::{ResultExt, maybe};
|
use util::{ResultExt, maybe};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(super) struct BreakpointList {
|
pub(crate) struct BreakpointList {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
breakpoint_store: Entity<BreakpointStore>,
|
breakpoint_store: Entity<BreakpointStore>,
|
||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
|
|
|
@ -68,6 +68,7 @@ pub async fn init_test_workspace(
|
||||||
workspace_handle
|
workspace_handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn active_debug_session_panel(
|
pub fn active_debug_session_panel(
|
||||||
workspace: WindowHandle<Workspace>,
|
workspace: WindowHandle<Workspace>,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
|
|
|
@ -81,6 +81,8 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
cx.run_until_parked();
|
||||||
|
|
||||||
// assert we have a debug panel item before the session has stopped
|
// assert we have a debug panel item before the session has stopped
|
||||||
workspace
|
workspace
|
||||||
.update(cx, |workspace, _window, cx| {
|
.update(cx, |workspace, _window, cx| {
|
||||||
|
@ -229,6 +231,8 @@ async fn test_we_can_only_have_one_panel_per_debug_session(
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
cx.run_until_parked();
|
||||||
|
|
||||||
// assert we have a debug panel item before the session has stopped
|
// assert we have a debug panel item before the session has stopped
|
||||||
workspace
|
workspace
|
||||||
.update(cx, |workspace, _window, cx| {
|
.update(cx, |workspace, _window, cx| {
|
||||||
|
@ -1052,6 +1056,8 @@ async fn test_debug_panel_item_thread_status_reset_on_failure(
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
cx.run_until_parked();
|
||||||
|
|
||||||
let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| {
|
let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| {
|
||||||
item.mode()
|
item.mode()
|
||||||
.as_running()
|
.as_running()
|
||||||
|
|
|
@ -1538,6 +1538,8 @@ async fn test_variable_list_only_sends_requests_when_rendering(
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
cx.run_until_parked();
|
||||||
|
|
||||||
let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| {
|
let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| {
|
||||||
let state = item
|
let state = item
|
||||||
.mode()
|
.mode()
|
||||||
|
|
|
@ -30,7 +30,8 @@ use dap::{
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use futures::{FutureExt, future::Shared};
|
use futures::{FutureExt, future::Shared};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
|
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,
|
||||||
|
Task, WeakEntity,
|
||||||
};
|
};
|
||||||
use rpc::AnyProtoClient;
|
use rpc::AnyProtoClient;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
@ -125,6 +126,7 @@ type UpstreamProjectId = u64;
|
||||||
struct RemoteConnection {
|
struct RemoteConnection {
|
||||||
_client: AnyProtoClient,
|
_client: AnyProtoClient,
|
||||||
_upstream_project_id: UpstreamProjectId,
|
_upstream_project_id: UpstreamProjectId,
|
||||||
|
_adapter_name: SharedString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteConnection {
|
impl RemoteConnection {
|
||||||
|
@ -996,6 +998,7 @@ impl Session {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode: Mode::Remote(RemoteConnection {
|
mode: Mode::Remote(RemoteConnection {
|
||||||
|
_adapter_name: SharedString::new(""), // todo(debugger) we need to pipe in the right values to deserialize the debugger pane layout
|
||||||
_client: client,
|
_client: client,
|
||||||
_upstream_project_id: upstream_project_id,
|
_upstream_project_id: upstream_project_id,
|
||||||
}),
|
}),
|
||||||
|
@ -1044,6 +1047,13 @@ impl Session {
|
||||||
&self.capabilities
|
&self.capabilities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn adapter_name(&self) -> SharedString {
|
||||||
|
match &self.mode {
|
||||||
|
Mode::Local(local_mode) => local_mode.adapter.name().into(),
|
||||||
|
Mode::Remote(remote_mode) => remote_mode._adapter_name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configuration(&self) -> Option<DebugAdapterConfig> {
|
pub fn configuration(&self) -> Option<DebugAdapterConfig> {
|
||||||
if let Mode::Local(local_mode) = &self.mode {
|
if let Mode::Local(local_mode) = &self.mode {
|
||||||
Some(local_mode.config.clone())
|
Some(local_mode.config.clone())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue