use std::sync::Arc; use anyhow::{Ok, Result, anyhow}; use dap::{ Capabilities, ContinueArguments, ExceptionFilterOptions, InitializeRequestArguments, InitializeRequestArgumentsPathFormat, NextArguments, SetVariableResponse, SourceBreakpoint, StepInArguments, StepOutArguments, SteppingGranularity, ValueFormat, Variable, VariablesArgumentsFilter, client::SessionId, proto_conversions::ProtoConversion, requests::{Continue, Next}, }; use rpc::proto; use serde_json::Value; use util::ResultExt; pub trait LocalDapCommand: 'static + Send + Sync + std::fmt::Debug { type Response: 'static + Send + std::fmt::Debug; type DapRequest: 'static + Send + dap::requests::Request; fn is_supported(_capabilities: &Capabilities) -> bool { true } fn to_dap(&self) -> ::Arguments; fn response_from_dap( &self, message: ::Response, ) -> Result; } pub trait DapCommand: LocalDapCommand { type ProtoRequest: 'static + Send; type ProtoResponse: 'static + Send; const CACHEABLE: bool = false; #[allow(dead_code)] fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId; #[allow(dead_code)] fn from_proto(request: &Self::ProtoRequest) -> Self; #[allow(unused)] fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest; #[allow(dead_code)] fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse; #[allow(unused)] fn response_from_proto(&self, message: Self::ProtoResponse) -> Result; } impl LocalDapCommand for Arc { type Response = T::Response; type DapRequest = T::DapRequest; fn is_supported(capabilities: &Capabilities) -> bool { T::is_supported(capabilities) } fn to_dap(&self) -> ::Arguments { T::to_dap(self) } fn response_from_dap( &self, message: ::Response, ) -> Result { T::response_from_dap(self, message) } } impl DapCommand for Arc { type ProtoRequest = T::ProtoRequest; type ProtoResponse = T::ProtoResponse; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { T::client_id_from_proto(request) } fn from_proto(request: &Self::ProtoRequest) -> Self { Arc::new(T::from_proto(request)) } fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { T::to_proto(self, debug_client_id, upstream_project_id) } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { T::response_to_proto(debug_client_id, message) } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { T::response_from_proto(self, message) } } #[derive(Debug, Hash, PartialEq, Eq)] pub struct StepCommand { pub thread_id: u64, pub granularity: Option, pub single_thread: Option, } impl StepCommand { fn from_proto(message: proto::DapNextRequest) -> Self { const LINE: i32 = proto::SteppingGranularity::Line as i32; const INSTRUCTION: i32 = proto::SteppingGranularity::Instruction as i32; let granularity = message.granularity.map(|granularity| match granularity { LINE => SteppingGranularity::Line, INSTRUCTION => SteppingGranularity::Instruction, _ => SteppingGranularity::Statement, }); Self { thread_id: message.thread_id, granularity, single_thread: message.single_thread, } } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct NextCommand { pub inner: StepCommand, } impl LocalDapCommand for NextCommand { type Response = ::Response; type DapRequest = Next; fn to_dap(&self) -> ::Arguments { NextArguments { thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for NextCommand { type ProtoRequest = proto::DapNextRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { inner: StepCommand::from_proto(request.clone()), } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapNextRequest { proto::DapNextRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity.map(|gran| gran.to_proto() as i32), } } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct StepInCommand { pub inner: StepCommand, } impl LocalDapCommand for StepInCommand { type Response = ::Response; type DapRequest = dap::requests::StepIn; fn to_dap(&self) -> ::Arguments { StepInArguments { thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, target_id: None, granularity: self.inner.granularity, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for StepInCommand { type ProtoRequest = proto::DapStepInRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { inner: StepCommand::from_proto(proto::DapNextRequest { project_id: request.project_id, client_id: request.client_id, thread_id: request.thread_id, single_thread: request.single_thread, granularity: request.granularity, }), } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapStepInRequest { proto::DapStepInRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity.map(|gran| gran.to_proto() as i32), target_id: None, } } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct StepOutCommand { pub inner: StepCommand, } impl LocalDapCommand for StepOutCommand { type Response = ::Response; type DapRequest = dap::requests::StepOut; fn to_dap(&self) -> ::Arguments { StepOutArguments { thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for StepOutCommand { type ProtoRequest = proto::DapStepOutRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { inner: StepCommand::from_proto(proto::DapNextRequest { project_id: request.project_id, client_id: request.client_id, thread_id: request.thread_id, single_thread: request.single_thread, granularity: request.granularity, }), } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapStepOutRequest { proto::DapStepOutRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity.map(|gran| gran.to_proto() as i32), } } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct StepBackCommand { pub inner: StepCommand, } impl LocalDapCommand for StepBackCommand { type Response = ::Response; type DapRequest = dap::requests::StepBack; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_step_back.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::StepBackArguments { thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for StepBackCommand { type ProtoRequest = proto::DapStepBackRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { inner: StepCommand::from_proto(proto::DapNextRequest { project_id: request.project_id, client_id: request.client_id, thread_id: request.thread_id, single_thread: request.single_thread, granularity: request.granularity, }), } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapStepBackRequest { proto::DapStepBackRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.inner.thread_id, single_thread: self.inner.single_thread, granularity: self.inner.granularity.map(|gran| gran.to_proto() as i32), } } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct ContinueCommand { pub args: ContinueArguments, } impl LocalDapCommand for ContinueCommand { type Response = ::Response; type DapRequest = Continue; fn to_dap(&self) -> ::Arguments { self.args.clone() } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } impl DapCommand for ContinueCommand { type ProtoRequest = proto::DapContinueRequest; type ProtoResponse = proto::DapContinueResponse; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapContinueRequest { proto::DapContinueRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.args.thread_id, single_thread: self.args.single_thread, } } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { args: ContinueArguments { thread_id: request.thread_id, single_thread: request.single_thread, }, } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(Self::Response { all_threads_continued: message.all_threads_continued, }) } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapContinueResponse { client_id: debug_client_id.to_proto(), all_threads_continued: message.all_threads_continued, } } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct PauseCommand { pub thread_id: u64, } impl LocalDapCommand for PauseCommand { type Response = ::Response; type DapRequest = dap::requests::Pause; fn to_dap(&self) -> ::Arguments { dap::PauseArguments { thread_id: self.thread_id, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for PauseCommand { type ProtoRequest = proto::DapPauseRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { thread_id: request.thread_id, } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapPauseRequest { proto::DapPauseRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.thread_id, } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct DisconnectCommand { pub restart: Option, pub terminate_debuggee: Option, pub suspend_debuggee: Option, } impl LocalDapCommand for DisconnectCommand { type Response = ::Response; type DapRequest = dap::requests::Disconnect; fn to_dap(&self) -> ::Arguments { dap::DisconnectArguments { restart: self.restart, terminate_debuggee: self.terminate_debuggee, suspend_debuggee: self.suspend_debuggee, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for DisconnectCommand { type ProtoRequest = proto::DapDisconnectRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { restart: request.restart, terminate_debuggee: request.terminate_debuggee, suspend_debuggee: request.suspend_debuggee, } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapDisconnectRequest { proto::DapDisconnectRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), restart: self.restart, terminate_debuggee: self.terminate_debuggee, suspend_debuggee: self.suspend_debuggee, } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct TerminateThreadsCommand { pub thread_ids: Option>, } impl LocalDapCommand for TerminateThreadsCommand { type Response = ::Response; type DapRequest = dap::requests::TerminateThreads; fn is_supported(capabilities: &Capabilities) -> bool { capabilities .supports_terminate_threads_request .unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::TerminateThreadsArguments { thread_ids: self.thread_ids.clone(), } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for TerminateThreadsCommand { type ProtoRequest = proto::DapTerminateThreadsRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { let thread_ids = if request.thread_ids.is_empty() { None } else { Some(request.thread_ids.clone()) }; Self { thread_ids } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapTerminateThreadsRequest { proto::DapTerminateThreadsRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_ids: self.thread_ids.clone().unwrap_or_default(), } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct TerminateCommand { pub restart: Option, } impl LocalDapCommand for TerminateCommand { type Response = ::Response; type DapRequest = dap::requests::Terminate; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_terminate_request.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::TerminateArguments { restart: self.restart, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for TerminateCommand { type ProtoRequest = proto::DapTerminateRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { restart: request.restart, } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapTerminateRequest { proto::DapTerminateRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), restart: self.restart, } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct RestartCommand { pub raw: serde_json::Value, } impl LocalDapCommand for RestartCommand { type Response = ::Response; type DapRequest = dap::requests::Restart; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_restart_request.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::RestartArguments { raw: self.raw.clone(), } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for RestartCommand { type ProtoRequest = proto::DapRestartRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { raw: serde_json::from_slice(&request.raw_args) .log_err() .unwrap_or(serde_json::Value::Null), } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapRestartRequest { let raw_args = serde_json::to_vec(&self.raw).log_err().unwrap_or_default(); proto::DapRestartRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), raw_args, } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Hash, PartialEq, Eq)] pub struct VariablesCommand { pub variables_reference: u64, pub filter: Option, pub start: Option, pub count: Option, pub format: Option, } impl LocalDapCommand for VariablesCommand { type Response = Vec; type DapRequest = dap::requests::Variables; fn to_dap(&self) -> ::Arguments { dap::VariablesArguments { variables_reference: self.variables_reference, filter: self.filter, start: self.start, count: self.count, format: self.format.clone(), } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.variables) } } impl DapCommand for VariablesCommand { type ProtoRequest = proto::VariablesRequest; type ProtoResponse = proto::DapVariables; const CACHEABLE: bool = true; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::VariablesRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), variables_reference: self.variables_reference, filter: None, start: self.start, count: self.count, format: None, } } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { variables_reference: request.variables_reference, filter: None, start: request.start, count: request.count, format: None, } } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapVariables { client_id: debug_client_id.to_proto(), variables: message.to_proto(), } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(Vec::from_proto(message.variables)) } } #[derive(Debug, Hash, PartialEq, Eq)] pub(crate) struct SetVariableValueCommand { pub name: String, pub value: String, pub variables_reference: u64, } impl LocalDapCommand for SetVariableValueCommand { type Response = SetVariableResponse; type DapRequest = dap::requests::SetVariable; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_set_variable.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::SetVariableArguments { format: None, name: self.name.clone(), value: self.value.clone(), variables_reference: self.variables_reference, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } impl DapCommand for SetVariableValueCommand { type ProtoRequest = proto::DapSetVariableValueRequest; type ProtoResponse = proto::DapSetVariableValueResponse; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapSetVariableValueRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), variables_reference: self.variables_reference, value: self.value.clone(), name: self.name.clone(), } } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { variables_reference: request.variables_reference, name: request.name.clone(), value: request.value.clone(), } } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapSetVariableValueResponse { client_id: debug_client_id.to_proto(), value: message.value, variable_type: message.type_, named_variables: message.named_variables, variables_reference: message.variables_reference, indexed_variables: message.indexed_variables, memory_reference: message.memory_reference, } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(SetVariableResponse { value: message.value, type_: message.variable_type, variables_reference: message.variables_reference, named_variables: message.named_variables, indexed_variables: message.indexed_variables, memory_reference: message.memory_reference, value_location_reference: None, // TODO }) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct RestartStackFrameCommand { pub stack_frame_id: u64, } impl LocalDapCommand for RestartStackFrameCommand { type Response = ::Response; type DapRequest = dap::requests::RestartFrame; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_restart_frame.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::RestartFrameArguments { frame_id: self.stack_frame_id, } } fn response_from_dap( &self, _message: ::Response, ) -> Result { Ok(()) } } impl DapCommand for RestartStackFrameCommand { type ProtoRequest = proto::DapRestartStackFrameRequest; type ProtoResponse = proto::Ack; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { stack_frame_id: request.stack_frame_id, } } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapRestartStackFrameRequest { proto::DapRestartStackFrameRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), stack_frame_id: self.stack_frame_id, } } fn response_to_proto( _debug_client_id: SessionId, _message: Self::Response, ) -> Self::ProtoResponse { proto::Ack {} } fn response_from_proto(&self, _message: Self::ProtoResponse) -> Result { Ok(()) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct ModulesCommand; impl LocalDapCommand for ModulesCommand { type Response = Vec; type DapRequest = dap::requests::Modules; fn is_supported(capabilities: &Capabilities) -> bool { capabilities.supports_modules_request.unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::ModulesArguments { start_module: None, module_count: None, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.modules) } } impl DapCommand for ModulesCommand { type ProtoRequest = proto::DapModulesRequest; type ProtoResponse = proto::DapModulesResponse; const CACHEABLE: bool = true; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(_request: &Self::ProtoRequest) -> Self { Self {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapModulesRequest { proto::DapModulesRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), } } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapModulesResponse { modules: message .into_iter() .map(|module| module.to_proto()) .collect(), client_id: debug_client_id.to_proto(), } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(message .modules .into_iter() .filter_map(|module| dap::Module::from_proto(module).ok()) .collect()) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct LoadedSourcesCommand; impl LocalDapCommand for LoadedSourcesCommand { type Response = Vec; type DapRequest = dap::requests::LoadedSources; fn is_supported(capabilities: &Capabilities) -> bool { capabilities .supports_loaded_sources_request .unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::LoadedSourcesArguments {} } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.sources) } } impl DapCommand for LoadedSourcesCommand { type ProtoRequest = proto::DapLoadedSourcesRequest; type ProtoResponse = proto::DapLoadedSourcesResponse; const CACHEABLE: bool = true; fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(_request: &Self::ProtoRequest) -> Self { Self {} } fn to_proto( &self, debug_client_id: SessionId, upstream_project_id: u64, ) -> proto::DapLoadedSourcesRequest { proto::DapLoadedSourcesRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), } } fn response_to_proto( debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapLoadedSourcesResponse { sources: message .into_iter() .map(|source| source.to_proto()) .collect(), client_id: debug_client_id.to_proto(), } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(message .sources .into_iter() .map(dap::Source::from_proto) .collect()) } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct StackTraceCommand { pub thread_id: u64, pub start_frame: Option, pub levels: Option, } impl LocalDapCommand for StackTraceCommand { type Response = Vec; type DapRequest = dap::requests::StackTrace; fn to_dap(&self) -> ::Arguments { dap::StackTraceArguments { thread_id: self.thread_id, start_frame: self.start_frame, levels: self.levels, format: None, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.stack_frames) } } impl DapCommand for StackTraceCommand { type ProtoRequest = proto::DapStackTraceRequest; type ProtoResponse = proto::DapStackTraceResponse; const CACHEABLE: bool = true; fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapStackTraceRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), thread_id: self.thread_id, start_frame: self.start_frame, stack_trace_levels: self.levels, } } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { thread_id: request.thread_id, start_frame: request.start_frame, levels: request.stack_trace_levels, } } fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(message .frames .into_iter() .map(dap::StackFrame::from_proto) .collect()) } fn response_to_proto( _debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapStackTraceResponse { frames: message.to_proto(), } } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct ScopesCommand { pub stack_frame_id: u64, } impl LocalDapCommand for ScopesCommand { type Response = Vec; type DapRequest = dap::requests::Scopes; fn to_dap(&self) -> ::Arguments { dap::ScopesArguments { frame_id: self.stack_frame_id, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.scopes) } } impl DapCommand for ScopesCommand { type ProtoRequest = proto::DapScopesRequest; type ProtoResponse = proto::DapScopesResponse; const CACHEABLE: bool = true; fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapScopesRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), stack_frame_id: self.stack_frame_id, } } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { stack_frame_id: request.stack_frame_id, } } fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(Vec::from_proto(message.scopes)) } fn response_to_proto( _debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapScopesResponse { scopes: message.to_proto(), } } } impl LocalDapCommand for super::session::CompletionsQuery { type Response = dap::CompletionsResponse; type DapRequest = dap::requests::Completions; fn to_dap(&self) -> ::Arguments { dap::CompletionsArguments { text: self.query.clone(), frame_id: self.frame_id, column: self.column, line: None, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } fn is_supported(capabilities: &Capabilities) -> bool { capabilities .supports_completions_request .unwrap_or_default() } } impl DapCommand for super::session::CompletionsQuery { type ProtoRequest = proto::DapCompletionRequest; type ProtoResponse = proto::DapCompletionResponse; const CACHEABLE: bool = true; fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapCompletionRequest { client_id: debug_client_id.to_proto(), project_id: upstream_project_id, frame_id: self.frame_id, query: self.query.clone(), column: self.column, line: self.line, } } fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { query: request.query.clone(), frame_id: request.frame_id, column: request.column, line: request.line, } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(dap::CompletionsResponse { targets: Vec::from_proto(message.completions), }) } fn response_to_proto( _debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapCompletionResponse { client_id: _debug_client_id.to_proto(), completions: message.targets.to_proto(), } } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct EvaluateCommand { pub expression: String, pub frame_id: Option, pub context: Option, pub source: Option, } impl LocalDapCommand for EvaluateCommand { type Response = dap::EvaluateResponse; type DapRequest = dap::requests::Evaluate; fn to_dap(&self) -> ::Arguments { dap::EvaluateArguments { expression: self.expression.clone(), frame_id: self.frame_id, context: self.context.clone(), source: self.source.clone(), line: None, column: None, format: None, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } impl DapCommand for EvaluateCommand { type ProtoRequest = proto::DapEvaluateRequest; type ProtoResponse = proto::DapEvaluateResponse; fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapEvaluateRequest { client_id: debug_client_id.to_proto(), project_id: upstream_project_id, expression: self.expression.clone(), frame_id: self.frame_id, context: self .context .clone() .map(|context| context.to_proto().into()), } } fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn from_proto(request: &Self::ProtoRequest) -> Self { Self { expression: request.expression.clone(), frame_id: request.frame_id, context: Some(dap::EvaluateArgumentsContext::from_proto(request.context())), source: None, } } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(dap::EvaluateResponse { result: message.result.clone(), type_: message.evaluate_type.clone(), presentation_hint: None, variables_reference: message.variable_reference, named_variables: message.named_variables, indexed_variables: message.indexed_variables, memory_reference: message.memory_reference.clone(), value_location_reference: None, //TODO }) } fn response_to_proto( _debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapEvaluateResponse { result: message.result, evaluate_type: message.type_, variable_reference: message.variables_reference, named_variables: message.named_variables, indexed_variables: message.indexed_variables, memory_reference: message.memory_reference, } } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct ThreadsCommand; impl LocalDapCommand for ThreadsCommand { type Response = Vec; type DapRequest = dap::requests::Threads; fn to_dap(&self) -> ::Arguments { dap::ThreadsArgument {} } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.threads) } } impl DapCommand for ThreadsCommand { type ProtoRequest = proto::DapThreadsRequest; type ProtoResponse = proto::DapThreadsResponse; const CACHEABLE: bool = true; fn to_proto(&self, debug_client_id: SessionId, upstream_project_id: u64) -> Self::ProtoRequest { proto::DapThreadsRequest { project_id: upstream_project_id, client_id: debug_client_id.to_proto(), } } fn from_proto(_request: &Self::ProtoRequest) -> Self { Self {} } fn client_id_from_proto(request: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(request.client_id) } fn response_from_proto(&self, message: Self::ProtoResponse) -> Result { Ok(Vec::from_proto(message.threads)) } fn response_to_proto( _debug_client_id: SessionId, message: Self::Response, ) -> Self::ProtoResponse { proto::DapThreadsResponse { threads: message.to_proto(), } } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) struct Initialize { pub(super) adapter_id: String, } fn dap_client_capabilities(adapter_id: String) -> InitializeRequestArguments { InitializeRequestArguments { client_id: Some("zed".to_owned()), client_name: Some("Zed".to_owned()), adapter_id, locale: Some("en-US".to_owned()), path_format: Some(InitializeRequestArgumentsPathFormat::Path), supports_variable_type: Some(true), supports_variable_paging: Some(false), supports_run_in_terminal_request: Some(true), supports_memory_references: Some(true), supports_progress_reporting: Some(false), supports_invalidated_event: Some(false), lines_start_at1: Some(true), columns_start_at1: Some(true), supports_memory_event: Some(false), supports_args_can_be_interpreted_by_shell: Some(false), supports_start_debugging_request: Some(true), supports_ansistyling: Some(false), } } impl LocalDapCommand for Initialize { type Response = Capabilities; type DapRequest = dap::requests::Initialize; fn to_dap(&self) -> ::Arguments { dap_client_capabilities(self.adapter_id.clone()) } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) struct ConfigurationDone {} impl LocalDapCommand for ConfigurationDone { type Response = (); type DapRequest = dap::requests::ConfigurationDone; fn is_supported(capabilities: &Capabilities) -> bool { capabilities .supports_configuration_done_request .unwrap_or_default() } fn to_dap(&self) -> ::Arguments { dap::ConfigurationDoneArguments {} } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) struct Launch { pub(super) raw: Value, } impl LocalDapCommand for Launch { type Response = (); type DapRequest = dap::requests::Launch; fn to_dap(&self) -> ::Arguments { dap::LaunchRequestArguments { raw: self.raw.clone(), } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) struct Attach { pub(super) raw: Value, } impl LocalDapCommand for Attach { type Response = (); type DapRequest = dap::requests::Attach; fn to_dap(&self) -> ::Arguments { dap::AttachRequestArguments { raw: self.raw.clone(), } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) struct SetBreakpoints { pub(super) source: dap::Source, pub(super) breakpoints: Vec, pub(super) source_modified: Option, } impl LocalDapCommand for SetBreakpoints { type Response = Vec; type DapRequest = dap::requests::SetBreakpoints; fn to_dap(&self) -> ::Arguments { dap::SetBreakpointsArguments { lines: None, source_modified: self.source_modified, source: self.source.clone(), breakpoints: Some(self.breakpoints.clone()), } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.breakpoints) } } #[derive(Clone, Debug, Hash, PartialEq)] pub(super) enum SetExceptionBreakpoints { Plain { filters: Vec, }, WithOptions { filters: Vec, }, } impl LocalDapCommand for SetExceptionBreakpoints { type Response = Vec; type DapRequest = dap::requests::SetExceptionBreakpoints; fn to_dap(&self) -> ::Arguments { match self { SetExceptionBreakpoints::Plain { filters } => dap::SetExceptionBreakpointsArguments { filters: filters.clone(), exception_options: None, filter_options: None, }, SetExceptionBreakpoints::WithOptions { filters } => { dap::SetExceptionBreakpointsArguments { filters: vec![], filter_options: Some(filters.clone()), exception_options: None, } } } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message.breakpoints.unwrap_or_default()) } } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub(super) struct LocationsCommand { pub(super) reference: u64, } impl LocalDapCommand for LocationsCommand { type Response = dap::LocationsResponse; type DapRequest = dap::requests::Locations; fn to_dap(&self) -> ::Arguments { dap::LocationsArguments { location_reference: self.reference, } } fn response_from_dap( &self, message: ::Response, ) -> Result { Ok(message) } } impl DapCommand for LocationsCommand { type ProtoRequest = proto::DapLocationsRequest; type ProtoResponse = proto::DapLocationsResponse; const CACHEABLE: bool = true; fn client_id_from_proto(message: &Self::ProtoRequest) -> SessionId { SessionId::from_proto(message.session_id) } fn from_proto(message: &Self::ProtoRequest) -> Self { Self { reference: message.location_reference, } } fn to_proto(&self, session_id: SessionId, project_id: u64) -> Self::ProtoRequest { proto::DapLocationsRequest { project_id, session_id: session_id.to_proto(), location_reference: self.reference, } } fn response_to_proto(_: SessionId, response: Self::Response) -> Self::ProtoResponse { proto::DapLocationsResponse { source: Some(response.source.to_proto()), line: response.line, column: response.column, end_line: response.end_line, end_column: response.end_column, } } fn response_from_proto(&self, response: Self::ProtoResponse) -> Result { Ok(dap::LocationsResponse { source: response .source .map(::from_proto) .ok_or_else(|| anyhow!("Missing `source` field in Locations proto"))?, line: response.line, column: response.column, end_line: response.end_line, end_column: response.end_column, }) } }