debugger: Fix breakpoint store RPC handlers not being registered correctly on SSH remotes (#44908)
Closes #36789 Release Notes: - Fixed setting breakpoints on remotes --------- Co-authored-by: Zed AI <ai@zed.dev>
This commit is contained in:
@@ -17,7 +17,7 @@ use std::{hash::Hash, ops::Range, path::Path, sync::Arc, u32};
|
||||
use text::{Point, PointUtf16};
|
||||
use util::maybe;
|
||||
|
||||
use crate::{Project, ProjectPath, buffer_store::BufferStore, worktree_store::WorktreeStore};
|
||||
use crate::{ProjectPath, buffer_store::BufferStore, worktree_store::WorktreeStore};
|
||||
|
||||
use super::session::ThreadId;
|
||||
|
||||
@@ -130,18 +130,12 @@ mod breakpoints_in_file {
|
||||
#[derive(Clone)]
|
||||
struct RemoteBreakpointStore {
|
||||
upstream_client: AnyProtoClient,
|
||||
_upstream_project_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LocalBreakpointStore {
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
upstream_project_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum BreakpointStoreMode {
|
||||
Local(LocalBreakpointStore),
|
||||
Local,
|
||||
Remote(RemoteBreakpointStore),
|
||||
}
|
||||
|
||||
@@ -155,6 +149,8 @@ pub struct ActiveStackFrame {
|
||||
}
|
||||
|
||||
pub struct BreakpointStore {
|
||||
buffer_store: Entity<BufferStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
breakpoints: BTreeMap<Arc<Path>, BreakpointsInFile>,
|
||||
downstream_client: Option<(AnyProtoClient, u64)>,
|
||||
active_stack_frame: Option<ActiveStackFrame>,
|
||||
@@ -170,28 +166,34 @@ impl BreakpointStore {
|
||||
pub fn local(worktree_store: Entity<WorktreeStore>, buffer_store: Entity<BufferStore>) -> Self {
|
||||
BreakpointStore {
|
||||
breakpoints: BTreeMap::new(),
|
||||
mode: BreakpointStoreMode::Local(LocalBreakpointStore {
|
||||
worktree_store,
|
||||
buffer_store,
|
||||
}),
|
||||
mode: BreakpointStoreMode::Local,
|
||||
buffer_store,
|
||||
worktree_store,
|
||||
downstream_client: None,
|
||||
active_stack_frame: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remote(upstream_project_id: u64, upstream_client: AnyProtoClient) -> Self {
|
||||
pub(crate) fn remote(
|
||||
upstream_project_id: u64,
|
||||
upstream_client: AnyProtoClient,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
) -> Self {
|
||||
BreakpointStore {
|
||||
breakpoints: BTreeMap::new(),
|
||||
mode: BreakpointStoreMode::Remote(RemoteBreakpointStore {
|
||||
upstream_client,
|
||||
_upstream_project_id: upstream_project_id,
|
||||
upstream_project_id,
|
||||
}),
|
||||
buffer_store,
|
||||
worktree_store,
|
||||
downstream_client: None,
|
||||
active_stack_frame: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shared(&mut self, project_id: u64, downstream_client: AnyProtoClient) {
|
||||
pub fn shared(&mut self, project_id: u64, downstream_client: AnyProtoClient) {
|
||||
self.downstream_client = Some((downstream_client, project_id));
|
||||
}
|
||||
|
||||
@@ -202,27 +204,31 @@ impl BreakpointStore {
|
||||
}
|
||||
|
||||
async fn handle_breakpoints_for_file(
|
||||
this: Entity<Project>,
|
||||
this: Entity<Self>,
|
||||
message: TypedEnvelope<proto::BreakpointsForFile>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
let breakpoints = cx.update(|cx| this.read(cx).breakpoint_store())?;
|
||||
if message.payload.breakpoints.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buffer = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
let path =
|
||||
this.project_path_for_absolute_path(message.payload.path.as_ref(), cx)?;
|
||||
Some(this.open_buffer(path, cx))
|
||||
let path = this
|
||||
.worktree_store
|
||||
.read(cx)
|
||||
.project_path_for_absolute_path(message.payload.path.as_ref(), cx)?;
|
||||
Some(
|
||||
this.buffer_store
|
||||
.update(cx, |this, cx| this.open_buffer(path, cx)),
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
.context("Invalid project path")?
|
||||
.await?;
|
||||
|
||||
breakpoints.update(&mut cx, move |this, cx| {
|
||||
this.update(&mut cx, move |this, cx| {
|
||||
let bps = this
|
||||
.breakpoints
|
||||
.entry(Arc::<Path>::from(message.payload.path.as_ref()))
|
||||
@@ -263,19 +269,20 @@ impl BreakpointStore {
|
||||
}
|
||||
|
||||
async fn handle_toggle_breakpoint(
|
||||
this: Entity<Project>,
|
||||
this: Entity<Self>,
|
||||
message: TypedEnvelope<proto::ToggleBreakpoint>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let breakpoints = this.read_with(&cx, |this, _| this.breakpoint_store())?;
|
||||
let path = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.project_path_for_absolute_path(message.payload.path.as_ref(), cx)
|
||||
this.worktree_store
|
||||
.read(cx)
|
||||
.project_path_for_absolute_path(message.payload.path.as_ref(), cx)
|
||||
})?
|
||||
.context("Could not resolve provided abs path")?;
|
||||
let buffer = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.buffer_store().read(cx).get_by_path(&path)
|
||||
this.buffer_store.read(cx).get_by_path(&path)
|
||||
})?
|
||||
.context("Could not find buffer for a given path")?;
|
||||
let breakpoint = message
|
||||
@@ -292,7 +299,7 @@ impl BreakpointStore {
|
||||
let breakpoint =
|
||||
Breakpoint::from_proto(breakpoint).context("Could not deserialize breakpoint")?;
|
||||
|
||||
breakpoints.update(&mut cx, |this, cx| {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.toggle_breakpoint(
|
||||
buffer,
|
||||
BreakpointWithPosition {
|
||||
@@ -547,7 +554,7 @@ impl BreakpointStore {
|
||||
.to_proto(&abs_path, &breakpoint.position, &HashMap::default())
|
||||
{
|
||||
cx.background_spawn(remote.upstream_client.request(proto::ToggleBreakpoint {
|
||||
project_id: remote._upstream_project_id,
|
||||
project_id: remote.upstream_project_id,
|
||||
path: abs_path.to_str().map(ToOwned::to_owned).unwrap(),
|
||||
breakpoint: Some(breakpoint),
|
||||
}))
|
||||
@@ -775,22 +782,21 @@ impl BreakpointStore {
|
||||
breakpoints: BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
|
||||
cx: &mut Context<BreakpointStore>,
|
||||
) -> Task<Result<()>> {
|
||||
if let BreakpointStoreMode::Local(mode) = &self.mode {
|
||||
let mode = mode.clone();
|
||||
if let BreakpointStoreMode::Local = &self.mode {
|
||||
let worktree_store = self.worktree_store.downgrade();
|
||||
let buffer_store = self.buffer_store.downgrade();
|
||||
cx.spawn(async move |this, cx| {
|
||||
let mut new_breakpoints = BTreeMap::default();
|
||||
for (path, bps) in breakpoints {
|
||||
if bps.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let (worktree, relative_path) = mode
|
||||
.worktree_store
|
||||
let (worktree, relative_path) = worktree_store
|
||||
.update(cx, |this, cx| {
|
||||
this.find_or_create_worktree(&path, false, cx)
|
||||
})?
|
||||
.await?;
|
||||
let buffer = mode
|
||||
.buffer_store
|
||||
let buffer = buffer_store
|
||||
.update(cx, |this, cx| {
|
||||
let path = ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
|
||||
@@ -832,6 +832,7 @@ enum EntitySubscription {
|
||||
LspStore(PendingEntitySubscription<LspStore>),
|
||||
SettingsObserver(PendingEntitySubscription<SettingsObserver>),
|
||||
DapStore(PendingEntitySubscription<DapStore>),
|
||||
BreakpointStore(PendingEntitySubscription<BreakpointStore>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -1378,8 +1379,14 @@ impl Project {
|
||||
});
|
||||
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
||||
|
||||
let breakpoint_store =
|
||||
cx.new(|_| BreakpointStore::remote(REMOTE_SERVER_PROJECT_ID, remote_proto.clone()));
|
||||
let breakpoint_store = cx.new(|_| {
|
||||
BreakpointStore::remote(
|
||||
REMOTE_SERVER_PROJECT_ID,
|
||||
remote_proto.clone(),
|
||||
buffer_store.clone(),
|
||||
worktree_store.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let dap_store = cx.new(|cx| {
|
||||
DapStore::new_remote(
|
||||
@@ -1475,6 +1482,7 @@ impl Project {
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.worktree_store);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.lsp_store);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.dap_store);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.breakpoint_store);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.settings_observer);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.git_store);
|
||||
remote_proto.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &this.agent_server_store);
|
||||
@@ -1496,6 +1504,7 @@ impl Project {
|
||||
TaskStore::init(Some(&remote_proto));
|
||||
ToolchainStore::init(&remote_proto);
|
||||
DapStore::init(&remote_proto, cx);
|
||||
BreakpointStore::init(&remote_proto);
|
||||
GitStore::init(&remote_proto);
|
||||
AgentServerStore::init_remote(&remote_proto);
|
||||
|
||||
@@ -1525,6 +1534,9 @@ impl Project {
|
||||
client.subscribe_to_entity::<SettingsObserver>(remote_id)?,
|
||||
),
|
||||
EntitySubscription::DapStore(client.subscribe_to_entity::<DapStore>(remote_id)?),
|
||||
EntitySubscription::BreakpointStore(
|
||||
client.subscribe_to_entity::<BreakpointStore>(remote_id)?,
|
||||
),
|
||||
];
|
||||
let committer = get_git_committer(&cx).await;
|
||||
let response = client
|
||||
@@ -1549,7 +1561,7 @@ impl Project {
|
||||
|
||||
async fn from_join_project_response(
|
||||
response: TypedEnvelope<proto::JoinProjectResponse>,
|
||||
subscriptions: [EntitySubscription; 7],
|
||||
subscriptions: [EntitySubscription; 8],
|
||||
client: Arc<Client>,
|
||||
run_tasks: bool,
|
||||
user_store: Entity<UserStore>,
|
||||
@@ -1583,8 +1595,14 @@ impl Project {
|
||||
|
||||
let environment =
|
||||
cx.new(|cx| ProjectEnvironment::new(None, worktree_store.downgrade(), None, true, cx))?;
|
||||
let breakpoint_store =
|
||||
cx.new(|_| BreakpointStore::remote(remote_id, client.clone().into()))?;
|
||||
let breakpoint_store = cx.new(|_| {
|
||||
BreakpointStore::remote(
|
||||
remote_id,
|
||||
client.clone().into(),
|
||||
buffer_store.clone(),
|
||||
worktree_store.clone(),
|
||||
)
|
||||
})?;
|
||||
let dap_store = cx.new(|cx| {
|
||||
DapStore::new_collab(
|
||||
remote_id,
|
||||
@@ -1707,7 +1725,7 @@ impl Project {
|
||||
remote_id,
|
||||
replica_id,
|
||||
},
|
||||
breakpoint_store,
|
||||
breakpoint_store: breakpoint_store.clone(),
|
||||
dap_store: dap_store.clone(),
|
||||
git_store: git_store.clone(),
|
||||
agent_server_store,
|
||||
@@ -1766,6 +1784,9 @@ impl Project {
|
||||
EntitySubscription::DapStore(subscription) => {
|
||||
subscription.set_entity(&dap_store, &cx)
|
||||
}
|
||||
EntitySubscription::BreakpointStore(subscription) => {
|
||||
subscription.set_entity(&breakpoint_store, &cx)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -4580,11 +4601,9 @@ impl Project {
|
||||
}
|
||||
|
||||
pub fn project_path_for_absolute_path(&self, abs_path: &Path, cx: &App) -> Option<ProjectPath> {
|
||||
self.find_worktree(abs_path, cx)
|
||||
.map(|(worktree, relative_path)| ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: relative_path,
|
||||
})
|
||||
self.worktree_store
|
||||
.read(cx)
|
||||
.project_path_for_absolute_path(abs_path, cx)
|
||||
}
|
||||
|
||||
pub fn get_workspace_root(&self, project_path: &ProjectPath, cx: &App) -> Option<PathBuf> {
|
||||
|
||||
@@ -169,6 +169,14 @@ impl WorktreeStore {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn project_path_for_absolute_path(&self, abs_path: &Path, cx: &App) -> Option<ProjectPath> {
|
||||
self.find_worktree(abs_path, cx)
|
||||
.map(|(worktree, relative_path)| ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: relative_path,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn absolutize(&self, project_path: &ProjectPath, cx: &App) -> Option<PathBuf> {
|
||||
let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
|
||||
Some(worktree.read(cx).absolutize(&project_path.path))
|
||||
|
||||
@@ -53,6 +53,7 @@ pub struct HeadlessProject {
|
||||
pub lsp_store: Entity<LspStore>,
|
||||
pub task_store: Entity<TaskStore>,
|
||||
pub dap_store: Entity<DapStore>,
|
||||
pub breakpoint_store: Entity<BreakpointStore>,
|
||||
pub agent_server_store: Entity<AgentServerStore>,
|
||||
pub settings_observer: Entity<SettingsObserver>,
|
||||
pub next_entry_id: Arc<AtomicUsize>,
|
||||
@@ -131,8 +132,13 @@ impl HeadlessProject {
|
||||
buffer_store
|
||||
});
|
||||
|
||||
let breakpoint_store =
|
||||
cx.new(|_| BreakpointStore::local(worktree_store.clone(), buffer_store.clone()));
|
||||
let breakpoint_store = cx.new(|_| {
|
||||
let mut breakpoint_store =
|
||||
BreakpointStore::local(worktree_store.clone(), buffer_store.clone());
|
||||
breakpoint_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone());
|
||||
|
||||
breakpoint_store
|
||||
});
|
||||
|
||||
let dap_store = cx.new(|cx| {
|
||||
let mut dap_store = DapStore::new_local(
|
||||
@@ -258,6 +264,7 @@ impl HeadlessProject {
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &task_store);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &toolchain_store);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &dap_store);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &breakpoint_store);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &settings_observer);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &git_store);
|
||||
session.subscribe_to_entity(REMOTE_SERVER_PROJECT_ID, &agent_server_store);
|
||||
@@ -301,7 +308,7 @@ impl HeadlessProject {
|
||||
ToolchainStore::init(&session);
|
||||
DapStore::init(&session, cx);
|
||||
// todo(debugger): Re init breakpoint store when we set it up for collab
|
||||
// BreakpointStore::init(&client);
|
||||
BreakpointStore::init(&session);
|
||||
GitStore::init(&session);
|
||||
AgentServerStore::init_headless(&session);
|
||||
|
||||
@@ -315,6 +322,7 @@ impl HeadlessProject {
|
||||
lsp_store,
|
||||
task_store,
|
||||
dap_store,
|
||||
breakpoint_store,
|
||||
agent_server_store,
|
||||
languages,
|
||||
extensions,
|
||||
|
||||
Reference in New Issue
Block a user