Stop routing session events via the DAP store (#29588)
This cleans up a bunch of indirection and will make it easier to show the session building state in the debugger terminal Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
fde1cc78a1
commit
15a83b5a10
@ -7,7 +7,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
|
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use collections::HashMap;
|
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
use dap::DebugRequest;
|
use dap::DebugRequest;
|
||||||
use dap::{
|
use dap::{
|
||||||
@ -15,7 +14,6 @@ use dap::{
|
|||||||
client::SessionId, debugger_settings::DebuggerSettings,
|
client::SessionId, debugger_settings::DebuggerSettings,
|
||||||
};
|
};
|
||||||
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
||||||
use futures::{SinkExt as _, channel::mpsc};
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, AsyncWindowContext, Context, DismissEvent, Entity, EntityId, EventEmitter,
|
Action, App, AsyncWindowContext, Context, DismissEvent, Entity, EntityId, EventEmitter,
|
||||||
FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity,
|
FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity,
|
||||||
@ -24,21 +22,11 @@ use gpui::{
|
|||||||
|
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use project::debugger::session::{Session, SessionStateEvent};
|
use project::debugger::session::{Session, SessionStateEvent};
|
||||||
use project::{
|
use project::{Project, debugger::session::ThreadStatus};
|
||||||
Project,
|
|
||||||
debugger::{
|
|
||||||
dap_store::{self, DapStore},
|
|
||||||
session::ThreadStatus,
|
|
||||||
},
|
|
||||||
terminals::TerminalKind,
|
|
||||||
};
|
|
||||||
use rpc::proto::{self};
|
use rpc::proto::{self};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::path::Path;
|
use task::{DebugScenario, TaskContext};
|
||||||
use std::sync::Arc;
|
|
||||||
use task::{DebugScenario, HideStrategy, RevealStrategy, RevealTarget, TaskContext, TaskId};
|
|
||||||
use terminal_view::TerminalView;
|
|
||||||
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
|
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
|
||||||
use workspace::SplitDirection;
|
use workspace::SplitDirection;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
@ -74,27 +62,21 @@ pub struct DebugPanel {
|
|||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||||
_subscriptions: Vec<Subscription>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugPanel {
|
impl DebugPanel {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
workspace: &Workspace,
|
workspace: &Workspace,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) -> Entity<Self> {
|
) -> Entity<Self> {
|
||||||
cx.new(|cx| {
|
cx.new(|cx| {
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let dap_store = project.read(cx).dap_store();
|
|
||||||
|
|
||||||
let _subscriptions =
|
|
||||||
vec![cx.subscribe_in(&dap_store, window, Self::handle_dap_store_event)];
|
|
||||||
|
|
||||||
let debug_panel = Self {
|
let debug_panel = Self {
|
||||||
size: px(300.),
|
size: px(300.),
|
||||||
sessions: vec![],
|
sessions: vec![],
|
||||||
active_session: None,
|
active_session: None,
|
||||||
_subscriptions,
|
|
||||||
past_debug_definition: None,
|
past_debug_definition: None,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
project,
|
project,
|
||||||
@ -288,7 +270,7 @@ impl DebugPanel {
|
|||||||
cx.subscribe_in(
|
cx.subscribe_in(
|
||||||
&session,
|
&session,
|
||||||
window,
|
window,
|
||||||
move |_, session, event: &SessionStateEvent, window, cx| match event {
|
move |this, session, event: &SessionStateEvent, window, cx| match event {
|
||||||
SessionStateEvent::Restart => {
|
SessionStateEvent::Restart => {
|
||||||
let mut curr_session = session.clone();
|
let mut curr_session = session.clone();
|
||||||
while let Some(parent_session) = curr_session
|
while let Some(parent_session) = curr_session
|
||||||
@ -310,6 +292,9 @@ impl DebugPanel {
|
|||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
SessionStateEvent::SpawnChildSession { request } => {
|
||||||
|
this.handle_start_debugging_request(request, session.clone(), window, cx);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -357,7 +342,7 @@ impl DebugPanel {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_child_session(
|
pub fn handle_start_debugging_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: &StartDebuggingRequestArguments,
|
request: &StartDebuggingRequestArguments,
|
||||||
parent_session: Entity<Session>,
|
parent_session: Entity<Session>,
|
||||||
@ -419,47 +404,6 @@ impl DebugPanel {
|
|||||||
self.active_session.clone()
|
self.active_session.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_dap_store_event(
|
|
||||||
&mut self,
|
|
||||||
_dap_store: &Entity<DapStore>,
|
|
||||||
event: &dap_store::DapStoreEvent,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
match event {
|
|
||||||
dap_store::DapStoreEvent::RunInTerminal {
|
|
||||||
session_id,
|
|
||||||
title,
|
|
||||||
cwd,
|
|
||||||
command,
|
|
||||||
args,
|
|
||||||
envs,
|
|
||||||
sender,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.handle_run_in_terminal_request(
|
|
||||||
*session_id,
|
|
||||||
title.clone(),
|
|
||||||
cwd.clone(),
|
|
||||||
command.clone(),
|
|
||||||
args.clone(),
|
|
||||||
envs.clone(),
|
|
||||||
sender.clone(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
dap_store::DapStoreEvent::SpawnChildSession {
|
|
||||||
request,
|
|
||||||
parent_session,
|
|
||||||
} => {
|
|
||||||
self.start_child_session(request, parent_session.clone(), window, cx);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_scenario(
|
pub fn resolve_scenario(
|
||||||
&self,
|
&self,
|
||||||
scenario: DebugScenario,
|
scenario: DebugScenario,
|
||||||
@ -529,101 +473,6 @@ impl DebugPanel {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_run_in_terminal_request(
|
|
||||||
&self,
|
|
||||||
session_id: SessionId,
|
|
||||||
title: Option<String>,
|
|
||||||
cwd: Option<Arc<Path>>,
|
|
||||||
command: Option<String>,
|
|
||||||
args: Vec<String>,
|
|
||||||
envs: HashMap<String, String>,
|
|
||||||
mut sender: mpsc::Sender<Result<u32>>,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> Task<Result<()>> {
|
|
||||||
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 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 {
|
|
||||||
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?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close_session(&mut self, entity_id: EntityId, window: &mut Window, cx: &mut Context<Self>) {
|
fn close_session(&mut self, entity_id: EntityId, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let Some(session) = self
|
let Some(session) = self
|
||||||
.sessions
|
.sessions
|
||||||
|
@ -104,12 +104,6 @@ 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) -> SharedString {
|
pub(crate) fn label(&self, cx: &App) -> SharedString {
|
||||||
if let Some(label) = self.label.get() {
|
if let Some(label) = self.label.get() {
|
||||||
return label.clone();
|
return label.clone();
|
||||||
@ -131,6 +125,13 @@ impl DebugSession {
|
|||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) fn running_state(&self) -> Entity<RunningState> {
|
||||||
|
match &self.mode {
|
||||||
|
DebugSessionState::Running(running_state) => running_state.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn label_element(&self, cx: &App) -> AnyElement {
|
pub(crate) fn label_element(&self, cx: &App) -> AnyElement {
|
||||||
let label = self.label(cx);
|
let label = self.label(cx);
|
||||||
|
|
||||||
|
@ -5,15 +5,20 @@ 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, time::Duration};
|
use std::{any::Any, ops::ControlFlow, path::PathBuf, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use crate::persistence::{self, DebuggerPaneItem, SerializedPaneLayout};
|
use crate::persistence::{self, DebuggerPaneItem, SerializedPaneLayout};
|
||||||
|
|
||||||
use super::DebugPanelItemEvent;
|
use super::DebugPanelItemEvent;
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
use breakpoint_list::BreakpointList;
|
use breakpoint_list::BreakpointList;
|
||||||
use collections::{HashMap, IndexMap};
|
use collections::{HashMap, IndexMap};
|
||||||
use console::Console;
|
use console::Console;
|
||||||
use dap::{Capabilities, Thread, client::SessionId, debugger_settings::DebuggerSettings};
|
use dap::{
|
||||||
|
Capabilities, RunInTerminalRequestArguments, Thread, client::SessionId,
|
||||||
|
debugger_settings::DebuggerSettings,
|
||||||
|
};
|
||||||
|
use futures::{SinkExt, channel::mpsc};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
|
Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||||
NoAction, Pixels, Point, Subscription, Task, WeakEntity,
|
NoAction, Pixels, Point, Subscription, Task, WeakEntity,
|
||||||
@ -23,8 +28,10 @@ use module_list::ModuleList;
|
|||||||
use project::{
|
use project::{
|
||||||
Project,
|
Project,
|
||||||
debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},
|
debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},
|
||||||
|
terminals::TerminalKind,
|
||||||
};
|
};
|
||||||
use rpc::proto::ViewId;
|
use rpc::proto::ViewId;
|
||||||
|
use serde_json::Value;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use stack_frame_list::StackFrameList;
|
use stack_frame_list::StackFrameList;
|
||||||
use terminal_view::TerminalView;
|
use terminal_view::TerminalView;
|
||||||
@ -32,7 +39,7 @@ use ui::{
|
|||||||
ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, ContextMenu,
|
ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, ContextMenu,
|
||||||
DropdownMenu, FluentBuilder, IconButton, IconName, IconSize, InteractiveElement, IntoElement,
|
DropdownMenu, FluentBuilder, IconButton, IconName, IconSize, InteractiveElement, IntoElement,
|
||||||
Label, LabelCommon as _, ParentElement, Render, SharedString, StatefulInteractiveElement,
|
Label, LabelCommon as _, ParentElement, Render, SharedString, StatefulInteractiveElement,
|
||||||
Styled, Tab, Tooltip, VisibleOnHover, Window, div, h_flex, v_flex,
|
Styled, Tab, Tooltip, VisibleOnHover, VisualContext, Window, div, h_flex, v_flex,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use variable_list::VariableList;
|
use variable_list::VariableList;
|
||||||
@ -559,6 +566,9 @@ impl RunningState {
|
|||||||
this.remove_pane_item(DebuggerPaneItem::LoadedSources, window, cx);
|
this.remove_pane_item(DebuggerPaneItem::LoadedSources, window, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SessionEvent::RunInTerminal { request, sender } => this
|
||||||
|
.handle_run_in_terminal(request, sender.clone(), window, cx)
|
||||||
|
.detach_and_log_err(cx),
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -657,6 +667,111 @@ impl RunningState {
|
|||||||
self.panes.pane_at_pixel_position(position).is_some()
|
self.panes.pane_at_pixel_position(position).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_run_in_terminal(
|
||||||
|
&self,
|
||||||
|
request: &RunInTerminalRequestArguments,
|
||||||
|
mut sender: mpsc::Sender<Result<u32>>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let running = cx.entity();
|
||||||
|
let Ok(project) = self
|
||||||
|
.workspace
|
||||||
|
.update(cx, |workspace, _| workspace.project().clone())
|
||||||
|
else {
|
||||||
|
return Task::ready(Err(anyhow!("no workspace")));
|
||||||
|
};
|
||||||
|
let session = self.session.read(cx);
|
||||||
|
|
||||||
|
let cwd = Some(&request.cwd)
|
||||||
|
.filter(|cwd| cwd.len() > 0)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| session.binary().cwd.clone());
|
||||||
|
|
||||||
|
let mut args = request.args.clone();
|
||||||
|
|
||||||
|
// Handle special case for NodeJS debug adapter
|
||||||
|
// If only the Node binary path is provided, we set the command to None
|
||||||
|
// This prevents the NodeJS REPL from appearing, which is not the desired behavior
|
||||||
|
// The expected usage is for users to provide their own Node command, e.g., `node test.js`
|
||||||
|
// This allows the NodeJS debug client to attach correctly
|
||||||
|
let command = if args.len() > 1 {
|
||||||
|
Some(args.remove(0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut envs: HashMap<String, String> = Default::default();
|
||||||
|
if let Some(Value::Object(env)) = &request.env {
|
||||||
|
for (key, value) in env {
|
||||||
|
let value_str = match (key.as_str(), value) {
|
||||||
|
(_, Value::String(value)) => value,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
envs.insert(key.clone(), value_str.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let shell = project.read(cx).terminal_settings(&cwd, cx).shell.clone();
|
||||||
|
let kind = if let Some(command) = command {
|
||||||
|
let title = request.title.clone().unwrap_or(command.clone());
|
||||||
|
TerminalKind::Task(task::SpawnInTerminal {
|
||||||
|
id: task::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: task::RevealStrategy::NoFocus,
|
||||||
|
reveal_target: task::RevealTarget::Dock,
|
||||||
|
hide: task::HideStrategy::Never,
|
||||||
|
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 weak_project = project.downgrade();
|
||||||
|
|
||||||
|
let terminal_task = 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, weak_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();
|
||||||
|
});
|
||||||
|
})?;
|
||||||
|
|
||||||
|
terminal.read_with(cx, |terminal, _| {
|
||||||
|
terminal
|
||||||
|
.pty_info
|
||||||
|
.pid()
|
||||||
|
.map(|pid| pid.as_u32())
|
||||||
|
.ok_or_else(|| anyhow!("Terminal was spawned but PID was not available"))
|
||||||
|
})?
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.background_spawn(async move { anyhow::Ok(sender.send(terminal_task.await).await?) })
|
||||||
|
}
|
||||||
|
|
||||||
fn create_sub_view(
|
fn create_sub_view(
|
||||||
&self,
|
&self,
|
||||||
item_kind: DebuggerPaneItem,
|
item_kind: DebuggerPaneItem,
|
||||||
|
@ -523,8 +523,8 @@ async fn test_handle_error_run_in_terminal_reverse_request(
|
|||||||
.fake_reverse_request::<RunInTerminal>(RunInTerminalRequestArguments {
|
.fake_reverse_request::<RunInTerminal>(RunInTerminalRequestArguments {
|
||||||
kind: None,
|
kind: None,
|
||||||
title: None,
|
title: None,
|
||||||
cwd: path!("/non-existing/path").into(), // invalid/non-existing path will cause the terminal spawn to fail
|
cwd: "".into(),
|
||||||
args: vec![],
|
args: vec!["oops".into(), "oops".into()],
|
||||||
env: None,
|
env: None,
|
||||||
args_can_be_interpreted_by_shell: None,
|
args_can_be_interpreted_by_shell: None,
|
||||||
})
|
})
|
||||||
|
@ -15,20 +15,16 @@ use async_trait::async_trait;
|
|||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use dap::{
|
use dap::{
|
||||||
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
|
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
|
||||||
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments,
|
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId,
|
||||||
Source, StackFrameId, StartDebuggingRequestArguments,
|
|
||||||
adapters::{
|
adapters::{
|
||||||
DapStatus, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
|
DapStatus, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
|
||||||
},
|
},
|
||||||
client::SessionId,
|
client::SessionId,
|
||||||
messages::Message,
|
messages::Message,
|
||||||
requests::{Completions, Evaluate, Request as _, RunInTerminal, StartDebugging},
|
requests::{Completions, Evaluate},
|
||||||
};
|
};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::{
|
use futures::future::{Shared, join_all};
|
||||||
channel::mpsc,
|
|
||||||
future::{Shared, join_all},
|
|
||||||
};
|
|
||||||
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task};
|
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task};
|
||||||
use http_client::HttpClient;
|
use http_client::HttpClient;
|
||||||
use language::{
|
use language::{
|
||||||
@ -43,9 +39,8 @@ use rpc::{
|
|||||||
AnyProtoClient, TypedEnvelope,
|
AnyProtoClient, TypedEnvelope,
|
||||||
proto::{self},
|
proto::{self},
|
||||||
};
|
};
|
||||||
use serde_json::Value;
|
|
||||||
use settings::{Settings, WorktreeId};
|
use settings::{Settings, WorktreeId};
|
||||||
use smol::{lock::Mutex, stream::StreamExt};
|
use smol::lock::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
@ -67,19 +62,6 @@ pub enum DapStoreEvent {
|
|||||||
session_id: SessionId,
|
session_id: SessionId,
|
||||||
message: Message,
|
message: Message,
|
||||||
},
|
},
|
||||||
RunInTerminal {
|
|
||||||
session_id: SessionId,
|
|
||||||
title: Option<String>,
|
|
||||||
cwd: Option<Arc<Path>>,
|
|
||||||
command: Option<String>,
|
|
||||||
args: Vec<String>,
|
|
||||||
envs: HashMap<String, String>,
|
|
||||||
sender: mpsc::Sender<Result<u32>>,
|
|
||||||
},
|
|
||||||
SpawnChildSession {
|
|
||||||
request: StartDebuggingRequestArguments,
|
|
||||||
parent_session: Entity<Session>,
|
|
||||||
},
|
|
||||||
Notification(String),
|
Notification(String),
|
||||||
RemoteHasInitialized,
|
RemoteHasInitialized,
|
||||||
}
|
}
|
||||||
@ -113,8 +95,6 @@ pub struct DapStore {
|
|||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
sessions: BTreeMap<SessionId, Entity<Session>>,
|
sessions: BTreeMap<SessionId, Entity<Session>>,
|
||||||
next_session_id: u32,
|
next_session_id: u32,
|
||||||
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
|
||||||
_start_debugging_task: Task<()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<DapStoreEvent> for DapStore {}
|
impl EventEmitter<DapStoreEvent> for DapStore {}
|
||||||
@ -184,35 +164,10 @@ impl DapStore {
|
|||||||
mode: DapStoreMode,
|
mode: DapStoreMode,
|
||||||
breakpoint_store: Entity<BreakpointStore>,
|
breakpoint_store: Entity<BreakpointStore>,
|
||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
cx: &mut Context<Self>,
|
_cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (start_debugging_tx, mut message_rx) =
|
|
||||||
futures::channel::mpsc::unbounded::<(SessionId, Message)>();
|
|
||||||
let task = cx.spawn(async move |this, cx| {
|
|
||||||
while let Some((session_id, message)) = message_rx.next().await {
|
|
||||||
match message {
|
|
||||||
Message::Request(request) => {
|
|
||||||
let _ = this
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
if request.command == StartDebugging::COMMAND {
|
|
||||||
this.handle_start_debugging_request(session_id, request, cx)
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
} else if request.command == RunInTerminal::COMMAND {
|
|
||||||
this.handle_run_in_terminal_request(session_id, request, cx)
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
mode,
|
mode,
|
||||||
_start_debugging_task: task,
|
|
||||||
start_debugging_tx,
|
|
||||||
next_session_id: 0,
|
next_session_id: 0,
|
||||||
downstream_client: None,
|
downstream_client: None,
|
||||||
breakpoint_store,
|
breakpoint_store,
|
||||||
@ -450,14 +405,11 @@ impl DapStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_debugging_tx = self.start_debugging_tx.clone();
|
|
||||||
|
|
||||||
let session = Session::new(
|
let session = Session::new(
|
||||||
self.breakpoint_store.clone(),
|
self.breakpoint_store.clone(),
|
||||||
session_id,
|
session_id,
|
||||||
parent_session,
|
parent_session,
|
||||||
template.clone(),
|
template.clone(),
|
||||||
start_debugging_tx,
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -469,7 +421,7 @@ impl DapStore {
|
|||||||
SessionStateEvent::Shutdown => {
|
SessionStateEvent::Shutdown => {
|
||||||
this.shutdown_session(session_id, cx).detach_and_log_err(cx);
|
this.shutdown_session(session_id, cx).detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
SessionStateEvent::Restart => {}
|
SessionStateEvent::Restart | SessionStateEvent::SpawnChildSession { .. } => {}
|
||||||
SessionStateEvent::Running => {
|
SessionStateEvent::Running => {
|
||||||
cx.emit(DapStoreEvent::DebugClientStarted(session_id));
|
cx.emit(DapStoreEvent::DebugClientStarted(session_id));
|
||||||
}
|
}
|
||||||
@ -583,196 +535,6 @@ impl DapStore {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_start_debugging_request(
|
|
||||||
&mut self,
|
|
||||||
session_id: SessionId,
|
|
||||||
request: dap::messages::Request,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> Task<Result<()>> {
|
|
||||||
let Some(parent_session) = self.session_by_id(session_id) else {
|
|
||||||
return Task::ready(Err(anyhow!("Session not found")));
|
|
||||||
};
|
|
||||||
let request_seq = request.seq;
|
|
||||||
|
|
||||||
let launch_request: Option<Result<StartDebuggingRequestArguments, _>> = request
|
|
||||||
.arguments
|
|
||||||
.as_ref()
|
|
||||||
.map(|value| serde_json::from_value(value.clone()));
|
|
||||||
|
|
||||||
let mut success = true;
|
|
||||||
if let Some(Ok(request)) = launch_request {
|
|
||||||
cx.emit(DapStoreEvent::SpawnChildSession {
|
|
||||||
request,
|
|
||||||
parent_session: parent_session.clone(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
log::error!(
|
|
||||||
"Failed to parse launch request arguments: {:?}",
|
|
||||||
request.arguments
|
|
||||||
);
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.spawn(async move |_, cx| {
|
|
||||||
parent_session
|
|
||||||
.update(cx, |session, cx| {
|
|
||||||
session.respond_to_client(
|
|
||||||
request_seq,
|
|
||||||
success,
|
|
||||||
StartDebugging::COMMAND.to_string(),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_run_in_terminal_request(
|
|
||||||
&mut self,
|
|
||||||
session_id: SessionId,
|
|
||||||
request: dap::messages::Request,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> Task<Result<()>> {
|
|
||||||
let Some(session) = self.session_by_id(session_id) else {
|
|
||||||
return Task::ready(Err(anyhow!("Session not found")));
|
|
||||||
};
|
|
||||||
|
|
||||||
let request_args = serde_json::from_value::<RunInTerminalRequestArguments>(
|
|
||||||
request.arguments.unwrap_or_default(),
|
|
||||||
)
|
|
||||||
.expect("To parse StartDebuggingRequestArguments");
|
|
||||||
|
|
||||||
let seq = request.seq;
|
|
||||||
|
|
||||||
let cwd = Path::new(&request_args.cwd);
|
|
||||||
|
|
||||||
match cwd.try_exists() {
|
|
||||||
Ok(false) | Err(_) if !request_args.cwd.is_empty() => {
|
|
||||||
return session.update(cx, |session, cx| {
|
|
||||||
session.respond_to_client(
|
|
||||||
seq,
|
|
||||||
false,
|
|
||||||
RunInTerminal::COMMAND.to_string(),
|
|
||||||
serde_json::to_value(dap::ErrorResponse {
|
|
||||||
error: Some(dap::Message {
|
|
||||||
id: seq,
|
|
||||||
format: format!("Received invalid/unknown cwd: {cwd:?}"),
|
|
||||||
variables: None,
|
|
||||||
send_telemetry: None,
|
|
||||||
show_user: None,
|
|
||||||
url: None,
|
|
||||||
url_label: None,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
let mut args = request_args.args.clone();
|
|
||||||
|
|
||||||
// Handle special case for NodeJS debug adapter
|
|
||||||
// If only the Node binary path is provided, we set the command to None
|
|
||||||
// This prevents the NodeJS REPL from appearing, which is not the desired behavior
|
|
||||||
// The expected usage is for users to provide their own Node command, e.g., `node test.js`
|
|
||||||
// This allows the NodeJS debug client to attach correctly
|
|
||||||
let command = if args.len() > 1 {
|
|
||||||
Some(args.remove(0))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut envs: HashMap<String, String> = Default::default();
|
|
||||||
if let Some(Value::Object(env)) = request_args.env {
|
|
||||||
for (key, value) in env {
|
|
||||||
let value_str = match (key.as_str(), value) {
|
|
||||||
(_, Value::String(value)) => value,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
envs.insert(key, value_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (tx, mut rx) = mpsc::channel::<Result<u32>>(1);
|
|
||||||
let cwd = Some(cwd)
|
|
||||||
.filter(|cwd| cwd.as_os_str().len() > 0)
|
|
||||||
.map(Arc::from)
|
|
||||||
.or_else(|| {
|
|
||||||
self.session_by_id(session_id)
|
|
||||||
.and_then(|session| session.read(cx).binary().cwd.as_deref().map(Arc::from))
|
|
||||||
});
|
|
||||||
cx.emit(DapStoreEvent::RunInTerminal {
|
|
||||||
session_id,
|
|
||||||
title: request_args.title,
|
|
||||||
cwd,
|
|
||||||
command,
|
|
||||||
args,
|
|
||||||
envs,
|
|
||||||
sender: tx,
|
|
||||||
});
|
|
||||||
cx.notify();
|
|
||||||
|
|
||||||
let session = session.downgrade();
|
|
||||||
cx.spawn(async move |_, cx| {
|
|
||||||
let (success, body) = match rx.next().await {
|
|
||||||
Some(Ok(pid)) => (
|
|
||||||
true,
|
|
||||||
serde_json::to_value(dap::RunInTerminalResponse {
|
|
||||||
process_id: None,
|
|
||||||
shell_process_id: Some(pid as u64),
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
),
|
|
||||||
Some(Err(error)) => (
|
|
||||||
false,
|
|
||||||
serde_json::to_value(dap::ErrorResponse {
|
|
||||||
error: Some(dap::Message {
|
|
||||||
id: seq,
|
|
||||||
format: error.to_string(),
|
|
||||||
variables: None,
|
|
||||||
send_telemetry: None,
|
|
||||||
show_user: None,
|
|
||||||
url: None,
|
|
||||||
url_label: None,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
),
|
|
||||||
None => (
|
|
||||||
false,
|
|
||||||
serde_json::to_value(dap::ErrorResponse {
|
|
||||||
error: Some(dap::Message {
|
|
||||||
id: seq,
|
|
||||||
format: "failed to receive response from spawn terminal".to_string(),
|
|
||||||
variables: None,
|
|
||||||
send_telemetry: None,
|
|
||||||
show_user: None,
|
|
||||||
url: None,
|
|
||||||
url_label: None,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.ok(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
session
|
|
||||||
.update(cx, |session, cx| {
|
|
||||||
session.respond_to_client(
|
|
||||||
seq,
|
|
||||||
success,
|
|
||||||
RunInTerminal::COMMAND.to_string(),
|
|
||||||
body,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evaluate(
|
pub fn evaluate(
|
||||||
&self,
|
&self,
|
||||||
session_id: &SessionId,
|
session_id: &SessionId,
|
||||||
|
@ -14,14 +14,18 @@ use anyhow::{Context as _, Result, anyhow};
|
|||||||
use collections::{HashMap, HashSet, IndexMap, IndexSet};
|
use collections::{HashMap, HashSet, IndexMap, IndexSet};
|
||||||
use dap::adapters::{DebugAdapterBinary, DebugTaskDefinition};
|
use dap::adapters::{DebugAdapterBinary, DebugTaskDefinition};
|
||||||
use dap::messages::Response;
|
use dap::messages::Response;
|
||||||
|
use dap::requests::{Request, RunInTerminal, StartDebugging};
|
||||||
use dap::{
|
use dap::{
|
||||||
Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
|
Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,
|
||||||
SteppingGranularity, StoppedEvent, VariableReference,
|
SteppingGranularity, StoppedEvent, VariableReference,
|
||||||
client::{DebugAdapterClient, SessionId},
|
client::{DebugAdapterClient, SessionId},
|
||||||
messages::{Events, Message},
|
messages::{Events, Message},
|
||||||
};
|
};
|
||||||
use dap::{ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory};
|
use dap::{
|
||||||
use futures::channel::oneshot;
|
ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory,
|
||||||
|
RunInTerminalRequestArguments, StartDebuggingRequestArguments,
|
||||||
|
};
|
||||||
|
use futures::channel::{mpsc, oneshot};
|
||||||
use futures::{FutureExt, future::Shared};
|
use futures::{FutureExt, future::Shared};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,
|
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,
|
||||||
@ -522,7 +526,6 @@ pub struct Session {
|
|||||||
is_session_terminated: bool,
|
is_session_terminated: bool,
|
||||||
requests: HashMap<TypeId, HashMap<RequestSlot, Shared<Task<Option<()>>>>>,
|
requests: HashMap<TypeId, HashMap<RequestSlot, Shared<Task<Option<()>>>>>,
|
||||||
exception_breakpoints: BTreeMap<String, (ExceptionBreakpointsFilter, IsEnabled)>,
|
exception_breakpoints: BTreeMap<String, (ExceptionBreakpointsFilter, IsEnabled)>,
|
||||||
start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
|
||||||
background_tasks: Vec<Task<()>>,
|
background_tasks: Vec<Task<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,13 +611,20 @@ pub enum SessionEvent {
|
|||||||
Threads,
|
Threads,
|
||||||
InvalidateInlineValue,
|
InvalidateInlineValue,
|
||||||
CapabilitiesLoaded,
|
CapabilitiesLoaded,
|
||||||
|
RunInTerminal {
|
||||||
|
request: RunInTerminalRequestArguments,
|
||||||
|
sender: mpsc::Sender<Result<u32>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum SessionStateEvent {
|
pub enum SessionStateEvent {
|
||||||
Running,
|
Running,
|
||||||
Shutdown,
|
Shutdown,
|
||||||
Restart,
|
Restart,
|
||||||
|
SpawnChildSession {
|
||||||
|
request: StartDebuggingRequestArguments,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<SessionEvent> for Session {}
|
impl EventEmitter<SessionEvent> for Session {}
|
||||||
@ -629,7 +639,6 @@ impl Session {
|
|||||||
session_id: SessionId,
|
session_id: SessionId,
|
||||||
parent_session: Option<Entity<Session>>,
|
parent_session: Option<Entity<Session>>,
|
||||||
template: DebugTaskDefinition,
|
template: DebugTaskDefinition,
|
||||||
start_debugging_requests_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Entity<Self> {
|
) -> Entity<Self> {
|
||||||
cx.new::<Self>(|cx| {
|
cx.new::<Self>(|cx| {
|
||||||
@ -678,7 +687,6 @@ impl Session {
|
|||||||
is_session_terminated: false,
|
is_session_terminated: false,
|
||||||
exception_breakpoints: Default::default(),
|
exception_breakpoints: Default::default(),
|
||||||
definition: template,
|
definition: template,
|
||||||
start_debugging_requests_tx,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this
|
this
|
||||||
@ -702,7 +710,6 @@ impl Session {
|
|||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let (message_tx, mut message_rx) = futures::channel::mpsc::unbounded();
|
let (message_tx, mut message_rx) = futures::channel::mpsc::unbounded();
|
||||||
let (initialized_tx, initialized_rx) = futures::channel::oneshot::channel();
|
let (initialized_tx, initialized_rx) = futures::channel::oneshot::channel();
|
||||||
let session_id = self.session_id();
|
|
||||||
|
|
||||||
let background_tasks = vec![cx.spawn(async move |this: WeakEntity<Session>, cx| {
|
let background_tasks = vec![cx.spawn(async move |this: WeakEntity<Session>, cx| {
|
||||||
let mut initialized_tx = Some(initialized_tx);
|
let mut initialized_tx = Some(initialized_tx);
|
||||||
@ -719,10 +726,15 @@ impl Session {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Message::Request(request) = message {
|
||||||
let Ok(Ok(_)) = this.update(cx, |this, _| {
|
let Ok(_) = this.update(cx, |this, cx| {
|
||||||
this.start_debugging_requests_tx
|
if request.command == StartDebugging::COMMAND {
|
||||||
.unbounded_send((session_id, message))
|
this.handle_start_debugging_request(request, cx)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
} else if request.command == RunInTerminal::COMMAND {
|
||||||
|
this.handle_run_in_terminal_request(request, cx)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
}) else {
|
}) else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
@ -830,6 +842,109 @@ impl Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_start_debugging_request(
|
||||||
|
&mut self,
|
||||||
|
request: dap::messages::Request,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let request_seq = request.seq;
|
||||||
|
|
||||||
|
let launch_request: Option<Result<StartDebuggingRequestArguments, _>> = request
|
||||||
|
.arguments
|
||||||
|
.as_ref()
|
||||||
|
.map(|value| serde_json::from_value(value.clone()));
|
||||||
|
|
||||||
|
let mut success = true;
|
||||||
|
if let Some(Ok(request)) = launch_request {
|
||||||
|
cx.emit(SessionStateEvent::SpawnChildSession { request });
|
||||||
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"Failed to parse launch request arguments: {:?}",
|
||||||
|
request.arguments
|
||||||
|
);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.spawn(async move |this, cx| {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.respond_to_client(
|
||||||
|
request_seq,
|
||||||
|
success,
|
||||||
|
StartDebugging::COMMAND.to_string(),
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_run_in_terminal_request(
|
||||||
|
&mut self,
|
||||||
|
request: dap::messages::Request,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let request_args = serde_json::from_value::<RunInTerminalRequestArguments>(
|
||||||
|
request.arguments.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.expect("To parse StartDebuggingRequestArguments");
|
||||||
|
|
||||||
|
let seq = request.seq;
|
||||||
|
|
||||||
|
let (tx, mut rx) = mpsc::channel::<Result<u32>>(1);
|
||||||
|
cx.emit(SessionEvent::RunInTerminal {
|
||||||
|
request: request_args,
|
||||||
|
sender: tx,
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
|
||||||
|
cx.spawn(async move |session, cx| {
|
||||||
|
let result = util::maybe!(async move {
|
||||||
|
rx.next().await.ok_or_else(|| {
|
||||||
|
anyhow!("failed to receive response from spawn terminal".to_string())
|
||||||
|
})?
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
let (success, body) = match result {
|
||||||
|
Ok(pid) => (
|
||||||
|
true,
|
||||||
|
serde_json::to_value(dap::RunInTerminalResponse {
|
||||||
|
process_id: None,
|
||||||
|
shell_process_id: Some(pid as u64),
|
||||||
|
})
|
||||||
|
.ok(),
|
||||||
|
),
|
||||||
|
Err(error) => (
|
||||||
|
false,
|
||||||
|
serde_json::to_value(dap::ErrorResponse {
|
||||||
|
error: Some(dap::Message {
|
||||||
|
id: seq,
|
||||||
|
format: error.to_string(),
|
||||||
|
variables: None,
|
||||||
|
send_telemetry: None,
|
||||||
|
show_user: None,
|
||||||
|
url: None,
|
||||||
|
url_label: None,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.ok(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
session
|
||||||
|
.update(cx, |session, cx| {
|
||||||
|
session.respond_to_client(
|
||||||
|
seq,
|
||||||
|
success,
|
||||||
|
RunInTerminal::COMMAND.to_string(),
|
||||||
|
body,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn request_initialize(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
pub(super) fn request_initialize(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||||
let adapter_id = String::from(self.definition.adapter.clone());
|
let adapter_id = String::from(self.definition.adapter.clone());
|
||||||
let request = Initialize { adapter_id };
|
let request = Initialize { adapter_id };
|
||||||
|
Loading…
Reference in New Issue
Block a user