debugger: Add memory view (#33955)

This is mostly setting up the UI for now; I expect it to be the biggest
chunk of work.

Release Notes:

- debugger: Added memory view

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
Piotr Osiewicz
2025-07-14 16:32:06 +02:00
committed by GitHub
co-authored by Anthony Eid Mikayla Maki Mikayla Maki
parent a2f5c47e2d
commit 6673c7cd4c
18 changed files with 1732 additions and 71 deletions
@@ -1,6 +1,7 @@
use std::sync::Arc;
use anyhow::{Context as _, Ok, Result};
use base64::Engine;
use dap::{
Capabilities, ContinueArguments, ExceptionFilterOptions, InitializeRequestArguments,
InitializeRequestArgumentsPathFormat, NextArguments, SetVariableResponse, SourceBreakpoint,
@@ -1774,3 +1775,95 @@ impl DapCommand for LocationsCommand {
})
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub(crate) struct ReadMemory {
pub(crate) memory_reference: String,
pub(crate) offset: Option<u64>,
pub(crate) count: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ReadMemoryResponse {
pub(super) address: Arc<str>,
pub(super) unreadable_bytes: Option<u64>,
pub(super) content: Arc<[u8]>,
}
impl LocalDapCommand for ReadMemory {
type Response = ReadMemoryResponse;
type DapRequest = dap::requests::ReadMemory;
const CACHEABLE: bool = true;
fn is_supported(capabilities: &Capabilities) -> bool {
capabilities
.supports_read_memory_request
.unwrap_or_default()
}
fn to_dap(&self) -> <Self::DapRequest as dap::requests::Request>::Arguments {
dap::ReadMemoryArguments {
memory_reference: self.memory_reference.clone(),
offset: self.offset,
count: self.count,
}
}
fn response_from_dap(
&self,
message: <Self::DapRequest as dap::requests::Request>::Response,
) -> Result<Self::Response> {
let data = if let Some(data) = message.data {
base64::engine::general_purpose::STANDARD
.decode(data)
.log_err()
.context("parsing base64 data from DAP's ReadMemory response")?
} else {
vec![]
};
Ok(ReadMemoryResponse {
address: message.address.into(),
content: data.into(),
unreadable_bytes: message.unreadable_bytes,
})
}
}
impl LocalDapCommand for dap::DataBreakpointInfoArguments {
type Response = dap::DataBreakpointInfoResponse;
type DapRequest = dap::requests::DataBreakpointInfo;
const CACHEABLE: bool = true;
fn is_supported(capabilities: &Capabilities) -> bool {
capabilities.supports_data_breakpoints.unwrap_or_default()
}
fn to_dap(&self) -> <Self::DapRequest as dap::requests::Request>::Arguments {
self.clone()
}
fn response_from_dap(
&self,
message: <Self::DapRequest as dap::requests::Request>::Response,
) -> Result<Self::Response> {
Ok(message)
}
}
impl LocalDapCommand for dap::WriteMemoryArguments {
type Response = dap::WriteMemoryResponse;
type DapRequest = dap::requests::WriteMemory;
fn is_supported(capabilities: &Capabilities) -> bool {
capabilities
.supports_write_memory_request
.unwrap_or_default()
}
fn to_dap(&self) -> <Self::DapRequest as dap::requests::Request>::Arguments {
self.clone()
}
fn response_from_dap(
&self,
message: <Self::DapRequest as dap::requests::Request>::Response,
) -> Result<Self::Response> {
Ok(message)
}
}