2041 lines · 64.6 KB · Rust
RawDownload
use crate::project_settings::ProjectSettings; use super::breakpoint_store::{BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason};use super::dap_command::{    self, Attach, ConfigurationDone, ContinueCommand, DapCommand, DisconnectCommand,    EvaluateCommand, Initialize, Launch, LoadedSourcesCommand, LocalDapCommand, LocationsCommand,    ModulesCommand, NextCommand, PauseCommand, RestartCommand, RestartStackFrameCommand,    ScopesCommand, SetVariableValueCommand, StackTraceCommand, StepBackCommand, StepCommand,    StepInCommand, StepOutCommand, TerminateCommand, TerminateThreadsCommand, ThreadsCommand,    VariablesCommand,};use super::dap_store::DapAdapterDelegate;use anyhow::{Context as _, Result, anyhow};use collections::{HashMap, HashSet, IndexMap, IndexSet};use dap::adapters::{DebugAdapter, DebugAdapterBinary};use dap::messages::Response;use dap::{    Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,    SteppingGranularity, StoppedEvent, VariableReference,    adapters::{DapDelegate, DapStatus},    client::{DebugAdapterClient, SessionId},    messages::{Events, Message},};use dap::{DapRegistry, DebugRequestType, OutputEventCategory};use futures::channel::oneshot;use futures::{FutureExt, future::Shared};use gpui::{    App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,};use rpc::AnyProtoClient;use serde_json::{Value, json};use settings::Settings;use smol::stream::StreamExt;use std::any::TypeId;use std::path::PathBuf;use std::u64;use std::{    any::Any,    collections::hash_map::Entry,    hash::{Hash, Hasher},    path::Path,    sync::Arc,};use task::{DebugAdapterConfig, DebugTaskDefinition};use text::{PointUtf16, ToPointUtf16};use util::{ResultExt, merge_json_value_into}; #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]#[repr(transparent)]pub struct ThreadId(pub u64); impl ThreadId {    pub const MIN: ThreadId = ThreadId(u64::MIN);    pub const MAX: ThreadId = ThreadId(u64::MAX);} impl From<u64> for ThreadId {    fn from(id: u64) -> Self {        Self(id)    }} #[derive(Clone, Debug)]pub struct StackFrame {    pub dap: dap::StackFrame,    pub scopes: Vec<dap::Scope>,} impl From<dap::StackFrame> for StackFrame {    fn from(stack_frame: dap::StackFrame) -> Self {        Self {            scopes: vec![],            dap: stack_frame,        }    }} #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub enum ThreadStatus {    #[default]    Running,    Stopped,    Stepping,    Exited,    Ended,} impl ThreadStatus {    pub fn label(&self) -> &'static str {        match self {            ThreadStatus::Running => "Running",            ThreadStatus::Stopped => "Stopped",            ThreadStatus::Stepping => "Stepping",            ThreadStatus::Exited => "Exited",            ThreadStatus::Ended => "Ended",        }    }} #[derive(Debug)]pub struct Thread {    dap: dap::Thread,    stack_frame_ids: IndexSet<StackFrameId>,    _has_stopped: bool,} impl From<dap::Thread> for Thread {    fn from(dap: dap::Thread) -> Self {        Self {            dap,            stack_frame_ids: Default::default(),            _has_stopped: false,        }    }} type UpstreamProjectId = u64; struct RemoteConnection {    _client: AnyProtoClient,    _upstream_project_id: UpstreamProjectId,} impl RemoteConnection {    fn send_proto_client_request<R: DapCommand>(        &self,        _request: R,        _session_id: SessionId,        cx: &mut App,    ) -> Task<Result<R::Response>> {        // let message = request.to_proto(session_id, self.upstream_project_id);        // let upstream_client = self.client.clone();        cx.background_executor().spawn(async move {            // debugger(todo): Properly send messages when we wrap dap_commands in envelopes again            // let response = upstream_client.request(message).await?;            // request.response_from_proto(response)            Err(anyhow!("Sending dap commands over RPC isn't supported yet"))        })    }     fn request<R: DapCommand>(        &self,        request: R,        session_id: SessionId,        cx: &mut App,    ) -> Task<Result<R::Response>>    where        <R::DapRequest as dap::requests::Request>::Response: 'static,        <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,    {        return self.send_proto_client_request::<R>(request, session_id, cx);    }} enum Mode {    Local(LocalMode),    Remote(RemoteConnection),} #[derive(Clone)]pub struct LocalMode {    client: Arc<DebugAdapterClient>,    config: DebugAdapterConfig,    adapter: Arc<dyn DebugAdapter>,    breakpoint_store: Entity<BreakpointStore>,} fn client_source(abs_path: &Path) -> dap::Source {    dap::Source {        name: abs_path            .file_name()            .map(|filename| filename.to_string_lossy().to_string()),        path: Some(abs_path.to_string_lossy().to_string()),        source_reference: None,        presentation_hint: None,        origin: None,        sources: None,        adapter_data: None,        checksums: None,    }} impl LocalMode {    fn new(        debug_adapters: Arc<DapRegistry>,        session_id: SessionId,        parent_session: Option<Entity<Session>>,        breakpoint_store: Entity<BreakpointStore>,        config: DebugAdapterConfig,        delegate: DapAdapterDelegate,        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,        cx: AsyncApp,    ) -> Task<Result<Self>> {        Self::new_inner(            debug_adapters,            session_id,            parent_session,            breakpoint_store,            config,            delegate,            messages_tx,            async |_, _| {},            cx,        )    }    #[cfg(any(test, feature = "test-support"))]    fn new_fake(        session_id: SessionId,        parent_session: Option<Entity<Session>>,        breakpoint_store: Entity<BreakpointStore>,        config: DebugAdapterConfig,        delegate: DapAdapterDelegate,        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,        caps: Capabilities,        fail: bool,        cx: AsyncApp,    ) -> Task<Result<Self>> {        use task::DebugRequestDisposition;         let request = match config.request.clone() {            DebugRequestDisposition::UserConfigured(request) => request,            DebugRequestDisposition::ReverseRequest(reverse_request_args) => {                match reverse_request_args.request {                    dap::StartDebuggingRequestArgumentsRequest::Launch => {                        DebugRequestType::Launch(task::LaunchConfig {                            program: "".to_owned(),                            cwd: None,                            args: Default::default(),                        })                    }                    dap::StartDebuggingRequestArgumentsRequest::Attach => {                        DebugRequestType::Attach(task::AttachConfig {                            process_id: Some(0),                        })                    }                }            }        };         let callback = async move |session: &mut LocalMode, cx: AsyncApp| {            session                .client                .on_request::<dap::requests::Initialize, _>(move |_, _| Ok(caps.clone()))                .await;             let paths = cx                .update(|cx| session.breakpoint_store.read(cx).breakpoint_paths())                .expect("Breakpoint store should exist in all tests that start debuggers");             session                .client                .on_request::<dap::requests::SetBreakpoints, _>(move |_, args| {                    let p = Arc::from(Path::new(&args.source.path.unwrap()));                    if !paths.contains(&p) {                        panic!("Sent breakpoints for path without any")                    }                     Ok(dap::SetBreakpointsResponse {                        breakpoints: Vec::default(),                    })                })                .await;             match request {                dap::DebugRequestType::Launch(_) => {                    if fail {                        session                            .client                            .on_request::<dap::requests::Launch, _>(move |_, _| {                                Err(dap::ErrorResponse {                                    error: Some(dap::Message {                                        id: 1,                                        format: "error".into(),                                        variables: None,                                        send_telemetry: None,                                        show_user: None,                                        url: None,                                        url_label: None,                                    }),                                })                            })                            .await;                    } else {                        session                            .client                            .on_request::<dap::requests::Launch, _>(move |_, _| Ok(()))                            .await;                    }                }                dap::DebugRequestType::Attach(attach_config) => {                    if fail {                        session                            .client                            .on_request::<dap::requests::Attach, _>(move |_, _| {                                Err(dap::ErrorResponse {                                    error: Some(dap::Message {                                        id: 1,                                        format: "error".into(),                                        variables: None,                                        send_telemetry: None,                                        show_user: None,                                        url: None,                                        url_label: None,                                    }),                                })                            })                            .await;                    } else {                        session                            .client                            .on_request::<dap::requests::Attach, _>(move |_, args| {                                assert_eq!(                                    json!({"request": "attach", "process_id": attach_config.process_id.unwrap()}),                                    args.raw                                );                                 Ok(())                            })                            .await;                    }                }            }             session                .client                .on_request::<dap::requests::Disconnect, _>(move |_, _| Ok(()))                .await;            session.client.fake_event(Events::Initialized(None)).await;        };        Self::new_inner(            DapRegistry::fake().into(),            session_id,            parent_session,            breakpoint_store,            config,            delegate,            messages_tx,            callback,            cx,        )    }    fn new_inner(        registry: Arc<DapRegistry>,        session_id: SessionId,        parent_session: Option<Entity<Session>>,        breakpoint_store: Entity<BreakpointStore>,        config: DebugAdapterConfig,        delegate: DapAdapterDelegate,        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,        on_initialized: impl AsyncFnOnce(&mut LocalMode, AsyncApp) + 'static,        cx: AsyncApp,    ) -> Task<Result<Self>> {        cx.spawn(async move |cx| {            let (adapter, binary) =                Self::get_adapter_binary(&registry, &config, &delegate, cx).await?;             let message_handler = Box::new(move |message| {                messages_tx.unbounded_send(message).ok();            });             let client = Arc::new(                if let Some(client) = parent_session                    .and_then(|session| cx.update(|cx| session.read(cx).adapter_client()).ok())                    .flatten()                {                    client                        .reconnect(session_id, binary, message_handler, cx.clone())                        .await?                } else {                    DebugAdapterClient::start(                        session_id,                        adapter.name(),                        binary,                        message_handler,                        cx.clone(),                    )                    .await                    .with_context(|| "Failed to start communication with debug adapter")?                },            );             let mut session = Self {                client,                adapter,                breakpoint_store,                config: config.clone(),            };             on_initialized(&mut session, cx.clone()).await;             Ok(session)        })    }     fn unset_breakpoints_from_paths(&self, paths: &Vec<Arc<Path>>, cx: &mut App) -> Task<()> {        let tasks: Vec<_> = paths            .into_iter()            .map(|path| {                self.request(                    dap_command::SetBreakpoints {                        source: client_source(path),                        source_modified: None,                        breakpoints: vec![],                    },                    cx.background_executor().clone(),                )            })            .collect();         cx.background_spawn(async move {            futures::future::join_all(tasks)                .await                .iter()                .for_each(|res| match res {                    Ok(_) => {}                    Err(err) => {                        log::warn!("Set breakpoints request failed: {}", err);                    }                });        })    }     fn send_breakpoints_from_path(        &self,        abs_path: Arc<Path>,        reason: BreakpointUpdatedReason,        cx: &mut App,    ) -> Task<()> {        let breakpoints = self            .breakpoint_store            .read_with(cx, |store, cx| store.breakpoints_from_path(&abs_path, cx))            .into_iter()            .filter(|bp| bp.state.is_enabled())            .map(Into::into)            .collect();         let task = self.request(            dap_command::SetBreakpoints {                source: client_source(&abs_path),                source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)),                breakpoints,            },            cx.background_executor().clone(),        );         cx.background_spawn(async move {            match task.await {                Ok(_) => {}                Err(err) => log::warn!("Set breakpoints request failed for path: {}", err),            }        })    }     fn send_all_breakpoints(&self, ignore_breakpoints: bool, cx: &App) -> Task<()> {        let mut breakpoint_tasks = Vec::new();        let breakpoints = self            .breakpoint_store            .read_with(cx, |store, cx| store.all_breakpoints(cx));         for (path, breakpoints) in breakpoints {            let breakpoints = if ignore_breakpoints {                vec![]            } else {                breakpoints                    .into_iter()                    .filter(|bp| bp.state.is_enabled())                    .map(Into::into)                    .collect()            };             breakpoint_tasks.push(self.request(                dap_command::SetBreakpoints {                    source: client_source(&path),                    source_modified: Some(false),                    breakpoints,                },                cx.background_executor().clone(),            ));        }         cx.background_spawn(async move {            futures::future::join_all(breakpoint_tasks)                .await                .iter()                .for_each(|res| match res {                    Ok(_) => {}                    Err(err) => {                        log::warn!("Set breakpoints request failed: {}", err);                    }                });        })    }     async fn get_adapter_binary(        registry: &Arc<DapRegistry>,        config: &DebugAdapterConfig,        delegate: &DapAdapterDelegate,        cx: &mut AsyncApp,    ) -> Result<(Arc<dyn DebugAdapter>, DebugAdapterBinary)> {        let adapter = registry

Lines 1–500 of 2041.