1973 lines · 61.7 KB · Rust
RawDownload
use super::breakpoint_store::{    BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,};use super::dap_command::{    self, Attach, ConfigurationDone, ContinueCommand, DapCommand, DisconnectCommand,    EvaluateCommand, Initialize, Launch, LoadedSourcesCommand, LocalDapCommand, LocationsCommand,    ModulesCommand, NextCommand, PauseCommand, RestartCommand, RestartStackFrameCommand,    ScopesCommand, SetExceptionBreakpoints, SetVariableValueCommand, StackTraceCommand,    StepBackCommand, StepCommand, StepInCommand, StepOutCommand, TerminateCommand,    TerminateThreadsCommand, ThreadsCommand, VariablesCommand,};use anyhow::{Context as _, Result, anyhow};use collections::{HashMap, HashSet, IndexMap, IndexSet};use dap::adapters::DebugAdapterBinary;use dap::messages::Response;use dap::{    Capabilities, ContinueArguments, EvaluateArgumentsContext, Module, Source, StackFrameId,    SteppingGranularity, StoppedEvent, VariableReference,    client::{DebugAdapterClient, SessionId},    messages::{Events, Message},};use dap::{ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory};use futures::channel::oneshot;use futures::{FutureExt, future::Shared};use gpui::{    App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,    Task, WeakEntity,};use rpc::AnyProtoClient;use serde_json::{Value, json};use smol::stream::StreamExt;use std::any::TypeId;use std::collections::BTreeMap;use std::u64;use std::{    any::Any,    collections::hash_map::Entry,    hash::{Hash, Hasher},    path::Path,    sync::Arc,};use task::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,    _adapter_name: SharedString,} 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>,    definition: DebugTaskDefinition,    binary: DebugAdapterBinary,    pub(crate) breakpoint_store: Entity<BreakpointStore>,    tmp_breakpoint: Option<SourceBreakpoint>,} 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(        session_id: SessionId,        parent_session: Option<Entity<Session>>,        breakpoint_store: Entity<BreakpointStore>,        config: DebugTaskDefinition,        binary: DebugAdapterBinary,        messages_tx: futures::channel::mpsc::UnboundedSender<Message>,        cx: AsyncApp,    ) -> Task<Result<Self>> {        Self::new_inner(            session_id,            parent_session,            breakpoint_store,            config,            binary,            messages_tx,            async |_, _| {},            cx,        )    }     fn new_inner(        session_id: SessionId,        parent_session: Option<Entity<Session>>,        breakpoint_store: Entity<BreakpointStore>,        config: DebugTaskDefinition,        binary: DebugAdapterBinary,        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 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.clone(), message_handler, cx.clone())                        .await?                } else {                    DebugAdapterClient::start(                        session_id,                        binary.clone(),                        message_handler,                        cx.clone(),                    )                    .await                    .with_context(|| "Failed to start communication with debug adapter")?                },            );             let mut session = Self {                client,                breakpoint_store,                tmp_breakpoint: None,                definition: config,                binary,            };             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())            .chain(self.tmp_breakpoint.clone())            .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_exception_breakpoints(        &self,        filters: Vec<ExceptionBreakpointsFilter>,        supports_filter_options: bool,        cx: &App,    ) -> Task<Result<Vec<dap::Breakpoint>>> {        let arg = if supports_filter_options {            SetExceptionBreakpoints::WithOptions {                filters: filters                    .into_iter()                    .map(|filter| ExceptionFilterOptions {                        filter_id: filter.filter,                        condition: None,                        mode: None,                    })                    .collect(),            }        } else {            SetExceptionBreakpoints::Plain {                filters: filters.into_iter().map(|filter| filter.filter).collect(),            }        };        self.request(arg, cx.background_executor().clone())    }    fn send_source_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);                    }                });        })    }     pub fn label(&self) -> String {        self.definition.label.clone()    }     fn request_initialization(&self, cx: &App) -> Task<Result<Capabilities>> {        let adapter_id = self.binary.adapter_name.to_string();         self.request(Initialize { adapter_id }, cx.background_executor().clone())    }     fn initialize_sequence(        &self,        capabilities: &Capabilities,        initialized_rx: oneshot::Receiver<()>,        cx: &App,    ) -> Task<Result<()>> {        let mut raw = self.binary.request_args.clone();         merge_json_value_into(            self.definition.initialize_args.clone().unwrap_or(json!({})),            &mut raw.configuration,        );         // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522        let launch = match raw.request {            dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(                Launch {                    raw: raw.configuration,                },                cx.background_executor().clone(),            ),            dap::StartDebuggingRequestArgumentsRequest::Attach => self.request(                Attach {                    raw: raw.configuration,                },                cx.background_executor().clone(),            ),        };         let configuration_done_supported = ConfigurationDone::is_supported(capabilities);        let exception_filters = capabilities            .exception_breakpoint_filters            .as_ref()            .map(|exception_filters| {                exception_filters                    .iter()                    .filter(|filter| filter.default == Some(true))                    .cloned()                    .collect::<Vec<_>>()            })            .unwrap_or_default();        let supports_exception_filters = capabilities            .supports_exception_filter_options            .unwrap_or_default();        let configuration_sequence = cx.spawn({            let this = self.clone();            async move |cx| {                initialized_rx.await?;                // todo(debugger) figure out if we want to handle a breakpoint response error                // This will probably consist of letting a user know that breakpoints failed to be set                cx.update(|cx| this.send_source_breakpoints(false, cx))?                    .await;                cx.update(|cx| {                    this.send_exception_breakpoints(                        exception_filters,                        supports_exception_filters,                        cx,                    )                })?                .await                .ok();                let ret = if configuration_done_supported {                    this.request(ConfigurationDone {}, cx.background_executor().clone())                } else {                    Task::ready(Ok(()))                }                .await;                ret            }        });         cx.background_spawn(async move {            futures::future::try_join(launch, configuration_sequence).await?;            Ok(())        })    }     fn request<R: LocalDapCommand>(        &self,        request: R,        executor: BackgroundExecutor,    ) -> Task<Result<R::Response>>    where        <R::DapRequest as dap::requests::Request>::Response: 'static,        <R::DapRequest as dap::requests::Request>::Arguments: 'static + Send,    {        let request = Arc::new(request);         let request_clone = request.clone();        let connection = self.client.clone();        let request_task = executor.spawn(async move {            let args = request_clone.to_dap();            connection.request::<R::DapRequest>(args).await        });         executor.spawn(async move {            let response = request.response_from_dap(request_task.await?);            response        })    }}impl From<RemoteConnection> for Mode {    fn from(value: RemoteConnection) -> Self {        Self::Remote(value)    }} impl Mode {    fn request_dap<R: DapCommand>(        &self,        session_id: SessionId,        request: R,        cx: &mut Context<Session>,

Lines 1–500 of 1973.