mirror of
https://github.com/zed-industries/zed.git
synced 2026-10-28 08:55:27 +08:00
Reveal in all multi buffers (#64154)
Closes https://github.com/zed-industries/zed/issues/62909 Closes https://github.com/zed-industries/zed/issues/57396 Release Notes: - Fixed project panel reveal not working in git diff-related multi buffers
This commit is contained in:
1 parent
f792c2d72d
commit
47b8ea5894
10 files changed
+145
-19
No files matched your search
@@ -118,6 +118,11 @@ impl AgentDiffPane {
|
||||
cx.subscribe(&thread, |this, _thread, event, cx| {
|
||||
this.handle_acp_thread_event(event, cx)
|
||||
}),
|
||||
cx.subscribe(&editor, |_, _, event: &EditorEvent, cx| {
|
||||
if event == &(EditorEvent::SelectionsChanged { local: true }) {
|
||||
cx.emit(event.clone())
|
||||
}
|
||||
}),
|
||||
],
|
||||
multibuffer,
|
||||
editor,
|
||||
|
||||
@@ -402,6 +402,7 @@ impl DiffMultibuffer {
|
||||
if !editor.focus_handle(cx).contains_focused(window, cx) {
|
||||
return;
|
||||
}
|
||||
cx.emit(event.clone());
|
||||
let Some(project_path) = self.active_project_path(cx) else {
|
||||
return;
|
||||
};
|
||||
@@ -720,12 +721,17 @@ impl DiffMultibuffer {
|
||||
|
||||
pub(crate) fn active_project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
let editor = self.editor.read(cx).focused_editor().read(cx);
|
||||
let multibuffer = editor.buffer().read(cx);
|
||||
let focused_multibuffer = editor.buffer().read(cx);
|
||||
let position = editor.selections.newest_anchor().head();
|
||||
let focused_snapshot = focused_multibuffer.snapshot(cx);
|
||||
let (focused_anchor, _) = focused_snapshot.anchor_to_buffer_anchor(position)?;
|
||||
let path = focused_snapshot.path_for_buffer(focused_anchor.buffer_id)?;
|
||||
|
||||
let multibuffer = self.multibuffer.read(cx);
|
||||
let position = multibuffer.location_for_path(path, cx)?;
|
||||
let snapshot = multibuffer.snapshot(cx);
|
||||
let (text_anchor, _) = snapshot.anchor_to_buffer_anchor(position)?;
|
||||
let buffer = multibuffer.buffer(text_anchor.buffer_id)?;
|
||||
|
||||
let file = buffer.read(cx).file()?;
|
||||
Some(ProjectPath {
|
||||
worktree_id: file.worktree_id(cx),
|
||||
|
||||
@@ -9723,8 +9723,8 @@ mod tests {
|
||||
use util::rel_path::rel_path;
|
||||
|
||||
use workspace::{
|
||||
ActivatePaneLeft, ActivatePaneRight, MultiWorkspace, ToolbarItemEvent, ToolbarItemLocation,
|
||||
item::test::TestItem,
|
||||
ActivatePaneLeft, ActivatePaneRight, ItemHandle as _, MultiWorkspace, ToolbarItemEvent,
|
||||
ToolbarItemLocation, item::test::TestItem,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
@@ -11380,6 +11380,11 @@ mod tests {
|
||||
.read_with(cx, |multi_workspace, _| multi_workspace.workspace().clone())
|
||||
.unwrap();
|
||||
let mut cx = VisualTestContext::from_window(window_handle.into(), cx);
|
||||
let project_path = project.read_with(&cx, |project, cx| {
|
||||
project
|
||||
.find_project_path(path!("/project/partial.rs"), cx)
|
||||
.expect("partial.rs should have a project path")
|
||||
});
|
||||
|
||||
cx.update(|_window, cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
@@ -11415,7 +11420,10 @@ mod tests {
|
||||
cx.run_until_parked();
|
||||
|
||||
workspace.read_with(&cx, |workspace, cx| {
|
||||
assert!(workspace.active_item_as::<StagedDiff>(cx).is_some());
|
||||
let staged_diff = workspace
|
||||
.active_item_as::<StagedDiff>(cx)
|
||||
.expect("StagedDiff should be active");
|
||||
assert_eq!(staged_diff.project_path(cx), Some(project_path.clone()));
|
||||
assert_eq!(workspace.items_of_type::<StagedDiff>(cx).count(), 1);
|
||||
assert_eq!(workspace.items_of_type::<UnstagedDiff>(cx).count(), 0);
|
||||
assert_eq!(workspace.items_of_type::<ProjectDiff>(cx).count(), 0);
|
||||
@@ -11440,6 +11448,7 @@ mod tests {
|
||||
let solo_diff = workspace
|
||||
.active_item_as::<SoloDiffView>(cx)
|
||||
.expect("SoloDiffView should be active");
|
||||
assert_eq!(solo_diff.project_path(cx), Some(project_path.clone()));
|
||||
let searchable = solo_diff
|
||||
.read(cx)
|
||||
.as_searchable(&solo_diff, cx)
|
||||
@@ -11484,7 +11493,10 @@ mod tests {
|
||||
cx.run_until_parked();
|
||||
|
||||
workspace.read_with(&cx, |workspace, cx| {
|
||||
assert!(workspace.active_item_as::<UnstagedDiff>(cx).is_some());
|
||||
let unstaged_diff = workspace
|
||||
.active_item_as::<UnstagedDiff>(cx)
|
||||
.expect("UnstagedDiff should be active");
|
||||
assert_eq!(unstaged_diff.project_path(cx), Some(project_path));
|
||||
assert_eq!(workspace.items_of_type::<StagedDiff>(cx).count(), 1);
|
||||
assert_eq!(workspace.items_of_type::<UnstagedDiff>(cx).count(), 1);
|
||||
assert_eq!(workspace.items_of_type::<ProjectDiff>(cx).count(), 0);
|
||||
|
||||
@@ -6,7 +6,7 @@ use editor::{
|
||||
use git_ui_core::file_diff_view::build_buffer_diff;
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FocusHandle, Focusable, Font,
|
||||
IntoElement, Render, SharedString, Task, Window,
|
||||
IntoElement, Render, SharedString, Subscription, Task, Window,
|
||||
};
|
||||
use language::{Buffer, Capability, HighlightedText, OffsetRangeExt};
|
||||
use multi_buffer::PathKey;
|
||||
@@ -28,6 +28,7 @@ use workspace::{
|
||||
pub struct MultiDiffView {
|
||||
editor: Entity<Editor>,
|
||||
file_count: usize,
|
||||
_editor_event_subscription: Subscription,
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
@@ -204,7 +205,17 @@ impl MultiDiffView {
|
||||
editor
|
||||
});
|
||||
|
||||
Self { editor, file_count }
|
||||
let editor_event_subscription = cx.subscribe(&editor, |_, _, event: &EditorEvent, cx| {
|
||||
if event == &(EditorEvent::SelectionsChanged { local: true }) {
|
||||
cx.emit(event.clone())
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
editor,
|
||||
file_count,
|
||||
_editor_event_subscription: editor_event_subscription,
|
||||
}
|
||||
}
|
||||
|
||||
fn title(&self) -> SharedString {
|
||||
|
||||
@@ -70,6 +70,7 @@ pub struct ProjectDiff {
|
||||
project: Entity<Project>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
diff: Entity<DiffMultibuffer>,
|
||||
_diff_event_subscription: Subscription,
|
||||
_diff_observation: Subscription,
|
||||
}
|
||||
|
||||
@@ -266,12 +267,18 @@ impl ProjectDiff {
|
||||
workspace: Entity<Workspace>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let observation = cx.observe(&diff, |_, _, cx| cx.notify());
|
||||
let diff_event_subscription = cx.subscribe(&diff, |_, _, event: &EditorEvent, cx| {
|
||||
if event == &(EditorEvent::SelectionsChanged { local: true }) {
|
||||
cx.emit(event.clone())
|
||||
}
|
||||
});
|
||||
let diff_observation = cx.observe(&diff, |_, _, cx| cx.notify());
|
||||
Self {
|
||||
project,
|
||||
workspace: workspace.downgrade(),
|
||||
diff,
|
||||
_diff_observation: observation,
|
||||
_diff_event_subscription: diff_event_subscription,
|
||||
_diff_observation: diff_observation,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1560,8 +1567,20 @@ mod tests {
|
||||
);
|
||||
|
||||
let project = Project::test(fs, [Path::new(path!("/a"))], cx).await;
|
||||
let (created_entry_id, changed_entry_id) = project.read_with(cx, |project, cx| {
|
||||
let entry_id = |path| {
|
||||
let project_path = project
|
||||
.find_project_path(path, cx)
|
||||
.expect("diff path should resolve");
|
||||
project
|
||||
.entry_for_path(&project_path, cx)
|
||||
.expect("diff path should have a project entry")
|
||||
.id
|
||||
};
|
||||
(entry_id(path!("/a/a.txt")), entry_id(path!("/a/b.txt")))
|
||||
});
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project, window, cx));
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
cx.run_until_parked();
|
||||
@@ -1591,6 +1610,10 @@ mod tests {
|
||||
ˇcreated
|
||||
"
|
||||
));
|
||||
assert_eq!(
|
||||
project.read_with(&cx.cx, |project, _| project.active_entry()),
|
||||
Some(created_entry_id)
|
||||
);
|
||||
|
||||
cx.dispatch_action(editor::actions::GoToPreviousHunk);
|
||||
|
||||
@@ -1605,6 +1628,10 @@ mod tests {
|
||||
created
|
||||
"
|
||||
));
|
||||
assert_eq!(
|
||||
project.read_with(&cx.cx, |project, _| project.active_entry()),
|
||||
None
|
||||
);
|
||||
|
||||
cx.dispatch_action(editor::actions::GoToPreviousHunk);
|
||||
|
||||
@@ -1619,6 +1646,42 @@ mod tests {
|
||||
created
|
||||
"
|
||||
));
|
||||
assert_eq!(
|
||||
project.read_with(&cx.cx, |project, _| project.active_entry()),
|
||||
Some(changed_entry_id)
|
||||
);
|
||||
|
||||
cx.cx.update(|_, cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.editor.diff_view_style = Some(DiffViewStyle::Split);
|
||||
});
|
||||
});
|
||||
});
|
||||
cx.cx.run_until_parked();
|
||||
|
||||
let lhs_editor = item.read_with(&cx.cx, |item, cx| {
|
||||
item.editor(cx)
|
||||
.read(cx)
|
||||
.lhs_editor()
|
||||
.cloned()
|
||||
.expect("split diff should have a left editor")
|
||||
});
|
||||
let mut lhs_cx = EditorTestContext::for_editor_in(lhs_editor, &mut cx.cx).await;
|
||||
lhs_cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges([multi_buffer::Anchor::Max..multi_buffer::Anchor::Max]);
|
||||
});
|
||||
});
|
||||
lhs_cx.update_editor(|editor, window, cx| {
|
||||
editor.change_selections(Default::default(), window, cx, |selections| {
|
||||
selections.select_ranges([multi_buffer::Anchor::Min..multi_buffer::Anchor::Min]);
|
||||
});
|
||||
});
|
||||
assert_eq!(
|
||||
project.read_with(&lhs_cx.cx, |project, _| project.active_entry()),
|
||||
Some(changed_entry_id)
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
@@ -18,7 +18,7 @@ use gpui::{
|
||||
use language::{Anchor, Buffer, HighlightedText, OffsetRangeExt as _, Point};
|
||||
use multi_buffer::{MultiBuffer, PathKey, excerpt_context_lines};
|
||||
use project::{
|
||||
Project,
|
||||
Project, ProjectPath,
|
||||
git_store::{Repository, RepositoryId},
|
||||
};
|
||||
use settings::{Settings, SettingsStore, StatusStyle};
|
||||
@@ -453,6 +453,10 @@ impl Item for SoloDiffView {
|
||||
self.editor.for_each_project_item(cx, f)
|
||||
}
|
||||
|
||||
fn active_project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
self.editor.read(cx).active_project_path(cx)
|
||||
}
|
||||
|
||||
fn set_nav_history(
|
||||
&mut self,
|
||||
nav_history: ItemNavHistory,
|
||||
|
||||
@@ -326,6 +326,10 @@ impl Item for StagedDiff {
|
||||
self.diff.read(cx).for_each_project_item(cx, f);
|
||||
}
|
||||
|
||||
fn active_project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
self.diff.read(cx).active_project_path(cx)
|
||||
}
|
||||
|
||||
fn set_nav_history(
|
||||
&mut self,
|
||||
nav_history: ItemNavHistory,
|
||||
|
||||
@@ -370,6 +370,10 @@ impl Item for UnstagedDiff {
|
||||
self.diff.read(cx).for_each_project_item(cx, f);
|
||||
}
|
||||
|
||||
fn active_project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
self.diff.read(cx).active_project_path(cx)
|
||||
}
|
||||
|
||||
fn set_nav_history(
|
||||
&mut self,
|
||||
nav_history: ItemNavHistory,
|
||||
|
||||
@@ -6475,6 +6475,21 @@ async fn test_autoreveal_follows_multibuffer_selection(cx: &mut gpui::TestAppCon
|
||||
"Moving the cursor into a different excerpt buffer should reveal that buffer's entry"
|
||||
);
|
||||
|
||||
cx.dispatch_action(workspace::RevealInProjectPanel::default());
|
||||
cx.run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..20, cx),
|
||||
&[
|
||||
"v project_root",
|
||||
" v dir_1",
|
||||
" file_1.py",
|
||||
" v dir_2",
|
||||
" file_2.py <== selected <== marked",
|
||||
],
|
||||
"Explicit reveal should use the project path for the excerpt under the cursor"
|
||||
);
|
||||
|
||||
// Wrappers re-emit inner-editor events through `to_item_events`, so a
|
||||
// benign `TitleChanged` (e.g. diagnostic summary updates) ultimately
|
||||
// reaches `Workspace::active_item_path_changed`. The active path should be
|
||||
|
||||
@@ -4554,16 +4554,18 @@ impl Render for Pane {
|
||||
))
|
||||
.on_action(cx.listener(
|
||||
|pane: &mut Self, action: &RevealInProjectPanel, _window, cx| {
|
||||
let active_item = pane.active_item();
|
||||
let entry_id = active_item.as_ref().and_then(|item| {
|
||||
action
|
||||
.entry_id
|
||||
.map(ProjectEntryId::from_proto)
|
||||
.or_else(|| item.project_entry_ids(cx).first().copied())
|
||||
});
|
||||
let entry_id = action.entry_id.map(ProjectEntryId::from_proto);
|
||||
let active_project_path =
|
||||
pane.active_item().and_then(|item| item.project_path(cx));
|
||||
|
||||
pane.project
|
||||
.update(cx, |project, cx| {
|
||||
let entry_id = entry_id.or_else(|| {
|
||||
active_project_path
|
||||
.as_ref()
|
||||
.and_then(|path| project.entry_for_path(path, cx))
|
||||
.map(|entry| entry.id)
|
||||
});
|
||||
if let Some(entry_id) = entry_id
|
||||
&& project
|
||||
.worktree_for_entry(entry_id, cx)
|
||||
|
||||
Reference in new issue
Block a user