mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 22:43:18 +00:00
sidebar: Remove View More batched thread expansion (#53956)
Remove the `DEFAULT_THREADS_SHOWN` limit and the View More / Collapse row from the sidebar thread list. When a project group is expanded, all of its threads are now shown. Fold/unfold of project group headers is preserved. ### Changes - Remove `ListEntry::ViewMore` variant and `render_view_more` - Remove `DEFAULT_THREADS_SHOWN`, `group_extra_batches`, `set_group_visible_thread_count`, `expand/collapse_thread_group` - Remove `visible_thread_count` from `ProjectGroupState`, `SerializedProjectGroupState`, `ProjectGroup`, and persistence - Remove `ShowMoreThreads` / `ShowFewerThreads` actions and vim keybindings cc @danilo-leal Release Notes: - N/A
This commit is contained in:
parent
3f2baed6a0
commit
3b2c12a111
5 changed files with 15 additions and 468 deletions
|
|
@ -48,8 +48,8 @@ use util::path_list::PathList;
|
|||
use workspace::{
|
||||
CloseWindow, FocusWorkspaceSidebar, MultiWorkspace, MultiWorkspaceEvent, NextProject,
|
||||
NextThread, Open, OpenMode, PreviousProject, PreviousThread, ProjectGroupKey, SaveIntent,
|
||||
ShowFewerThreads, ShowMoreThreads, Sidebar as WorkspaceSidebar, SidebarSide, Toast,
|
||||
ToggleWorkspaceSidebar, Workspace, notifications::NotificationId, sidebar_side_context_menu,
|
||||
Sidebar as WorkspaceSidebar, SidebarSide, Toast, ToggleWorkspaceSidebar, Workspace,
|
||||
notifications::NotificationId, sidebar_side_context_menu,
|
||||
};
|
||||
|
||||
use zed_actions::OpenRecent;
|
||||
|
|
@ -83,7 +83,6 @@ gpui::actions!(
|
|||
const DEFAULT_WIDTH: Pixels = px(300.0);
|
||||
const MIN_WIDTH: Pixels = px(200.0);
|
||||
const MAX_WIDTH: Pixels = px(800.0);
|
||||
const DEFAULT_THREADS_SHOWN: usize = 5;
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
enum SerializedSidebarView {
|
||||
|
|
@ -227,10 +226,6 @@ enum ListEntry {
|
|||
has_threads: bool,
|
||||
},
|
||||
Thread(ThreadEntry),
|
||||
ViewMore {
|
||||
key: ProjectGroupKey,
|
||||
is_fully_expanded: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -255,7 +250,6 @@ impl ListEntry {
|
|||
ListEntry::ProjectHeader { key, .. } => multi_workspace
|
||||
.workspaces_for_project_group(key, cx)
|
||||
.unwrap_or_default(),
|
||||
ListEntry::ViewMore { .. } => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -482,17 +476,6 @@ impl Sidebar {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn group_extra_batches(&self, key: &ProjectGroupKey, cx: &App) -> usize {
|
||||
self.multi_workspace
|
||||
.upgrade()
|
||||
.and_then(|mw| {
|
||||
mw.read(cx)
|
||||
.group_state_by_key(key)
|
||||
.and_then(|state| state.visible_thread_count)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn set_group_expanded(&self, key: &ProjectGroupKey, expanded: bool, cx: &mut Context<Self>) {
|
||||
if let Some(mw) = self.multi_workspace.upgrade() {
|
||||
mw.update(cx, |mw, cx| {
|
||||
|
|
@ -504,22 +487,6 @@ impl Sidebar {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_group_visible_thread_count(
|
||||
&self,
|
||||
key: &ProjectGroupKey,
|
||||
count: Option<usize>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(mw) = self.multi_workspace.upgrade() {
|
||||
mw.update(cx, |mw, cx| {
|
||||
if let Some(state) = mw.group_state_by_key_mut(key) {
|
||||
state.visible_thread_count = count;
|
||||
}
|
||||
mw.serialize(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active_workspace(&self, workspace: &Entity<Workspace>, cx: &App) -> bool {
|
||||
self.multi_workspace
|
||||
.upgrade()
|
||||
|
|
@ -1250,55 +1217,13 @@ impl Sidebar {
|
|||
continue;
|
||||
}
|
||||
|
||||
let total = threads.len();
|
||||
|
||||
let extra_batches = self.group_extra_batches(&group_key, cx);
|
||||
let threads_to_show =
|
||||
DEFAULT_THREADS_SHOWN + (extra_batches * DEFAULT_THREADS_SHOWN);
|
||||
let count = threads_to_show.min(total);
|
||||
|
||||
let mut promoted_threads: HashSet<agent_ui::ThreadId> = HashSet::new();
|
||||
|
||||
// Build visible entries in a single pass. Threads within
|
||||
// the cutoff are always shown. Threads beyond it are shown
|
||||
// only if they should be promoted (running, waiting, or
|
||||
// focused)
|
||||
for (index, thread) in threads.into_iter().enumerate() {
|
||||
let is_hidden = index >= count;
|
||||
|
||||
if is_hidden {
|
||||
let is_notified = notified_threads.contains(&thread.metadata.thread_id);
|
||||
let is_promoted = thread.status == AgentThreadStatus::Running
|
||||
|| thread.status == AgentThreadStatus::WaitingForConfirmation
|
||||
|| is_notified
|
||||
|| self.active_entry.as_ref().is_some_and(|active| {
|
||||
active.matches_entry(&ListEntry::Thread(thread.clone()))
|
||||
});
|
||||
if is_promoted {
|
||||
promoted_threads.insert(thread.metadata.thread_id);
|
||||
}
|
||||
let is_in_promoted = promoted_threads.contains(&thread.metadata.thread_id);
|
||||
if !is_in_promoted {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for thread in threads {
|
||||
if let Some(sid) = &thread.metadata.session_id {
|
||||
current_session_ids.insert(sid.clone());
|
||||
}
|
||||
current_thread_ids.insert(thread.metadata.thread_id);
|
||||
entries.push(thread.into());
|
||||
}
|
||||
|
||||
let visible = count + promoted_threads.len();
|
||||
let is_fully_expanded = visible >= total;
|
||||
|
||||
if total > DEFAULT_THREADS_SHOWN {
|
||||
entries.push(ListEntry::ViewMore {
|
||||
key: group_key.clone(),
|
||||
is_fully_expanded,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1414,10 +1339,6 @@ impl Sidebar {
|
|||
)
|
||||
}
|
||||
ListEntry::Thread(thread) => self.render_thread(ix, thread, is_active, is_selected, cx),
|
||||
ListEntry::ViewMore {
|
||||
key,
|
||||
is_fully_expanded,
|
||||
} => self.render_view_more(ix, key, *is_fully_expanded, is_selected, cx),
|
||||
};
|
||||
|
||||
if is_group_header_after_first {
|
||||
|
|
@ -1485,8 +1406,6 @@ impl Sidebar {
|
|||
};
|
||||
|
||||
let key_for_toggle = key.clone();
|
||||
let key_for_collapse = key.clone();
|
||||
let view_more_expanded = self.group_extra_batches(key, cx) > 0;
|
||||
|
||||
let label = if highlight_positions.is_empty() {
|
||||
Label::new(label.clone())
|
||||
|
|
@ -1632,30 +1551,6 @@ impl Sidebar {
|
|||
},
|
||||
))
|
||||
})
|
||||
.when(has_threads && view_more_expanded && !is_collapsed, |this| {
|
||||
this.child(
|
||||
IconButton::new(
|
||||
SharedString::from(format!(
|
||||
"{id_prefix}project-header-collapse-{ix}",
|
||||
)),
|
||||
IconName::ListCollapse,
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Show Fewer Threads"))
|
||||
.on_click(cx.listener({
|
||||
let key_for_collapse = key_for_collapse.clone();
|
||||
move |this, _, _window, cx| {
|
||||
this.selection = None;
|
||||
this.set_group_visible_thread_count(
|
||||
&key_for_collapse,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
this.update_entries(cx);
|
||||
}
|
||||
})),
|
||||
)
|
||||
})
|
||||
.child(self.render_project_header_ellipsis_menu(ix, id_prefix, key, cx)),
|
||||
)
|
||||
.tooltip(Tooltip::element({
|
||||
|
|
@ -2146,18 +2041,6 @@ impl Sidebar {
|
|||
}
|
||||
}
|
||||
}
|
||||
ListEntry::ViewMore {
|
||||
key,
|
||||
is_fully_expanded,
|
||||
..
|
||||
} => {
|
||||
let key = key.clone();
|
||||
if *is_fully_expanded {
|
||||
self.reset_thread_group_expansion(&key, cx);
|
||||
} else {
|
||||
self.expand_thread_group(&key, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2718,7 +2601,7 @@ impl Sidebar {
|
|||
self.update_entries(cx);
|
||||
}
|
||||
}
|
||||
Some(ListEntry::Thread(_) | ListEntry::ViewMore { .. }) => {
|
||||
Some(ListEntry::Thread(_)) => {
|
||||
for i in (0..ix).rev() {
|
||||
if let Some(ListEntry::ProjectHeader { key, .. }) = self.contents.entries.get(i)
|
||||
{
|
||||
|
|
@ -2745,7 +2628,7 @@ impl Sidebar {
|
|||
// Find the group header for the current selection.
|
||||
let header_ix = match self.contents.entries.get(ix) {
|
||||
Some(ListEntry::ProjectHeader { .. }) => Some(ix),
|
||||
Some(ListEntry::Thread(_) | ListEntry::ViewMore { .. }) => (0..ix).rev().find(|&i| {
|
||||
Some(ListEntry::Thread(_)) => (0..ix).rev().find(|&i| {
|
||||
matches!(
|
||||
self.contents.entries.get(i),
|
||||
Some(ListEntry::ProjectHeader { .. })
|
||||
|
|
@ -3448,7 +3331,6 @@ impl Sidebar {
|
|||
timestamp,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -3855,38 +3737,6 @@ impl Sidebar {
|
|||
.anchor(gpui::Corner::BottomRight)
|
||||
}
|
||||
|
||||
fn render_view_more(
|
||||
&self,
|
||||
ix: usize,
|
||||
key: &ProjectGroupKey,
|
||||
is_fully_expanded: bool,
|
||||
is_selected: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let key = key.clone();
|
||||
let id = SharedString::from(format!("view-more-{}", ix));
|
||||
|
||||
let label: SharedString = if is_fully_expanded {
|
||||
"Collapse".into()
|
||||
} else {
|
||||
"View More".into()
|
||||
};
|
||||
|
||||
ThreadItem::new(id, label)
|
||||
.focused(is_selected)
|
||||
.icon_visible(false)
|
||||
.title_label_color(Color::Muted)
|
||||
.on_click(cx.listener(move |this, _, _window, cx| {
|
||||
this.selection = None;
|
||||
if is_fully_expanded {
|
||||
this.reset_thread_group_expansion(&key, cx);
|
||||
} else {
|
||||
this.expand_thread_group(&key, cx);
|
||||
}
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn new_thread_in_group(
|
||||
&mut self,
|
||||
_: &NewThreadInGroup,
|
||||
|
|
@ -3943,7 +3793,7 @@ impl Sidebar {
|
|||
let ix = self.selection?;
|
||||
match self.contents.entries.get(ix) {
|
||||
Some(ListEntry::ProjectHeader { key, .. }) => Some(key.clone()),
|
||||
Some(ListEntry::Thread(_) | ListEntry::ViewMore { .. }) => {
|
||||
Some(ListEntry::Thread(_)) => {
|
||||
(0..ix)
|
||||
.rev()
|
||||
.find_map(|i| match self.contents.entries.get(i) {
|
||||
|
|
@ -4120,59 +3970,6 @@ impl Sidebar {
|
|||
self.cycle_thread_impl(false, window, cx);
|
||||
}
|
||||
|
||||
fn expand_thread_group(&mut self, project_group_key: &ProjectGroupKey, cx: &mut Context<Self>) {
|
||||
let current = self.group_extra_batches(project_group_key, cx);
|
||||
self.set_group_visible_thread_count(project_group_key, Some(current + 1), cx);
|
||||
self.update_entries(cx);
|
||||
}
|
||||
|
||||
fn reset_thread_group_expansion(
|
||||
&mut self,
|
||||
project_group_key: &ProjectGroupKey,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.set_group_visible_thread_count(project_group_key, None, cx);
|
||||
self.update_entries(cx);
|
||||
}
|
||||
|
||||
fn collapse_thread_group(
|
||||
&mut self,
|
||||
project_group_key: &ProjectGroupKey,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let batches = self.group_extra_batches(project_group_key, cx);
|
||||
match batches {
|
||||
0 => return,
|
||||
1 => self.set_group_visible_thread_count(project_group_key, None, cx),
|
||||
_ => self.set_group_visible_thread_count(project_group_key, Some(batches - 1), cx),
|
||||
}
|
||||
self.update_entries(cx);
|
||||
}
|
||||
|
||||
fn on_show_more_threads(
|
||||
&mut self,
|
||||
_: &ShowMoreThreads,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(active_key) = self.active_project_group_key(cx) else {
|
||||
return;
|
||||
};
|
||||
self.expand_thread_group(&active_key, cx);
|
||||
}
|
||||
|
||||
fn on_show_fewer_threads(
|
||||
&mut self,
|
||||
_: &ShowFewerThreads,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(active_key) = self.active_project_group_key(cx) else {
|
||||
return;
|
||||
};
|
||||
self.collapse_thread_group(&active_key, cx);
|
||||
}
|
||||
|
||||
fn render_no_results(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let has_query = self.has_filter_query(cx);
|
||||
let message = if has_query {
|
||||
|
|
@ -4729,8 +4526,6 @@ impl Render for Sidebar {
|
|||
.on_action(cx.listener(Self::on_previous_project))
|
||||
.on_action(cx.listener(Self::on_next_thread))
|
||||
.on_action(cx.listener(Self::on_previous_thread))
|
||||
.on_action(cx.listener(Self::on_show_more_threads))
|
||||
.on_action(cx.listener(Self::on_show_fewer_threads))
|
||||
.on_action(cx.listener(|this, _: &OpenRecent, window, cx| {
|
||||
this.recent_projects_popover_handle.toggle(window, cx);
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -145,11 +145,6 @@ fn assert_remote_project_integration_sidebar_state(
|
|||
title
|
||||
);
|
||||
}
|
||||
ListEntry::ViewMore { .. } => {
|
||||
panic!(
|
||||
"unexpected `View More` entry while simulating remote project integration flicker"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -427,15 +422,6 @@ fn visible_entries_as_strings(
|
|||
format!(" {title}{worktree}{live}{status_str}{notified}{selected}")
|
||||
}
|
||||
}
|
||||
ListEntry::ViewMore {
|
||||
is_fully_expanded, ..
|
||||
} => {
|
||||
if *is_fully_expanded {
|
||||
format!(" - Collapse{}", selected)
|
||||
} else {
|
||||
format!(" + View More{}", selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
|
@ -453,7 +439,7 @@ async fn test_serialization_round_trip(cx: &mut TestAppContext) {
|
|||
|
||||
let project_group_key = project.read_with(cx, |project, cx| project.project_group_key(cx));
|
||||
|
||||
// Set a custom width, collapse the group, and expand "View More".
|
||||
// Set a custom width and collapse the group.
|
||||
sidebar.update_in(cx, |sidebar, window, cx| {
|
||||
sidebar.set_width(Some(px(420.0)), cx);
|
||||
sidebar.toggle_collapse(&project_group_key, window, cx);
|
||||
|
|
@ -635,101 +621,6 @@ async fn test_workspace_lifecycle(cx: &mut TestAppContext) {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_view_more_pagination(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let sidebar = setup_sidebar(&multi_workspace, cx);
|
||||
|
||||
save_n_test_threads(12, &project, cx).await;
|
||||
|
||||
multi_workspace.update_in(cx, |_, _window, cx| cx.notify());
|
||||
cx.run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&sidebar, cx),
|
||||
vec![
|
||||
//
|
||||
"v [my-project]",
|
||||
" Thread 12",
|
||||
" Thread 11",
|
||||
" Thread 10",
|
||||
" Thread 9",
|
||||
" Thread 8",
|
||||
" + View More",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_view_more_batched_expansion(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let sidebar = setup_sidebar(&multi_workspace, cx);
|
||||
|
||||
// Create 17 threads: initially shows 5, then 10, then 15, then all 17 with Collapse
|
||||
save_n_test_threads(17, &project, cx).await;
|
||||
|
||||
let project_group_key = project.read_with(cx, |project, cx| project.project_group_key(cx));
|
||||
|
||||
multi_workspace.update_in(cx, |_, _window, cx| cx.notify());
|
||||
cx.run_until_parked();
|
||||
|
||||
// Initially shows 5 threads + View More
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 7); // header + 5 threads + View More
|
||||
assert!(entries.iter().any(|e| e.contains("View More")));
|
||||
|
||||
// Focus and navigate to View More, then confirm to expand by one batch
|
||||
focus_sidebar(&sidebar, cx);
|
||||
for _ in 0..7 {
|
||||
cx.dispatch_action(SelectNext);
|
||||
}
|
||||
cx.dispatch_action(Confirm);
|
||||
cx.run_until_parked();
|
||||
|
||||
// Now shows 10 threads + View More
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 12); // header + 10 threads + View More
|
||||
assert!(entries.iter().any(|e| e.contains("View More")));
|
||||
|
||||
// Expand again by one batch
|
||||
sidebar.update_in(cx, |s, _window, cx| {
|
||||
s.expand_thread_group(&project_group_key, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
// Now shows 15 threads + View More
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 17); // header + 15 threads + View More
|
||||
assert!(entries.iter().any(|e| e.contains("View More")));
|
||||
|
||||
// Expand one more time - should show all 17 threads with Collapse button
|
||||
sidebar.update_in(cx, |s, _window, cx| {
|
||||
s.expand_thread_group(&project_group_key, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
// All 17 threads shown with Collapse button
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 19); // header + 17 threads + Collapse
|
||||
assert!(!entries.iter().any(|e| e.contains("View More")));
|
||||
assert!(entries.iter().any(|e| e.contains("Collapse")));
|
||||
|
||||
// Click collapse - should go back to showing 5 threads
|
||||
sidebar.update_in(cx, |s, _window, cx| {
|
||||
s.reset_thread_group_expansion(&project_group_key, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
// Back to initial state: 5 threads + View More
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 7); // header + 5 threads + View More
|
||||
assert!(entries.iter().any(|e| e.contains("View More")));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_collapse_and_expand_group(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
|
|
@ -853,7 +744,6 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
|||
key: ProjectGroupKey::new(None, collapsed_path.clone()),
|
||||
workspaces: Vec::new(),
|
||||
expanded: false,
|
||||
visible_thread_count: None,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -992,11 +882,6 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
|||
worktrees: Vec::new(),
|
||||
diff_stats: DiffStats::default(),
|
||||
}),
|
||||
// View More entry
|
||||
ListEntry::ViewMore {
|
||||
key: ProjectGroupKey::new(None, expanded_path.clone()),
|
||||
is_fully_expanded: false,
|
||||
},
|
||||
// Collapsed project header
|
||||
ListEntry::ProjectHeader {
|
||||
key: ProjectGroupKey::new(None, collapsed_path.clone()),
|
||||
|
|
@ -1023,14 +908,13 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
|||
" Error thread * (error)",
|
||||
" Waiting thread (waiting)",
|
||||
" Notified thread * (!)",
|
||||
" + View More",
|
||||
"> [collapsed-project]",
|
||||
]
|
||||
);
|
||||
|
||||
// Move selection to the collapsed header
|
||||
sidebar.update_in(cx, |s, _window, _cx| {
|
||||
s.selection = Some(7);
|
||||
s.selection = Some(6);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -1219,40 +1103,6 @@ async fn test_keyboard_confirm_on_project_header_toggles_collapse(cx: &mut TestA
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_keyboard_confirm_on_view_more_expands(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let sidebar = setup_sidebar(&multi_workspace, cx);
|
||||
|
||||
save_n_test_threads(8, &project, cx).await;
|
||||
multi_workspace.update_in(cx, |_, _window, cx| cx.notify());
|
||||
cx.run_until_parked();
|
||||
|
||||
// Should show header + 5 threads + "View More"
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 7);
|
||||
assert!(entries.iter().any(|e| e.contains("View More")));
|
||||
|
||||
// Focus sidebar (selection starts at None), then navigate down to the "View More" entry (index 6)
|
||||
focus_sidebar(&sidebar, cx);
|
||||
for _ in 0..7 {
|
||||
cx.dispatch_action(SelectNext);
|
||||
}
|
||||
assert_eq!(sidebar.read_with(cx, |s, _| s.selection), Some(6));
|
||||
|
||||
// Confirm on "View More" to expand
|
||||
cx.dispatch_action(Confirm);
|
||||
cx.run_until_parked();
|
||||
|
||||
// All 8 threads should now be visible with a "Collapse" button
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(entries.len(), 10); // header + 8 threads + Collapse button
|
||||
assert!(!entries.iter().any(|e| e.contains("View More")));
|
||||
assert!(entries.iter().any(|e| e.contains("Collapse")));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_keyboard_expand_and_collapse_selected_entry(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
|
|
@ -1983,60 +1833,6 @@ async fn test_search_matches_workspace_name(cx: &mut TestAppContext) {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_finds_threads_hidden_behind_view_more(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
let (multi_workspace, cx) =
|
||||
cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
|
||||
let sidebar = setup_sidebar(&multi_workspace, cx);
|
||||
|
||||
// Create 8 threads. The oldest one has a unique name and will be
|
||||
// behind View More (only 5 shown by default).
|
||||
for i in 0..8u32 {
|
||||
let title = if i == 0 {
|
||||
"Hidden gem thread".to_string()
|
||||
} else {
|
||||
format!("Thread {}", i + 1)
|
||||
};
|
||||
save_thread_metadata(
|
||||
acp::SessionId::new(Arc::from(format!("thread-{}", i))),
|
||||
Some(title.into()),
|
||||
chrono::TimeZone::with_ymd_and_hms(&Utc, 2024, 1, 1, 0, 0, i).unwrap(),
|
||||
None,
|
||||
&project,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
cx.run_until_parked();
|
||||
|
||||
// Confirm the thread is not visible and View More is shown.
|
||||
let entries = visible_entries_as_strings(&sidebar, cx);
|
||||
assert!(
|
||||
entries.iter().any(|e| e.contains("View More")),
|
||||
"should have View More button"
|
||||
);
|
||||
assert!(
|
||||
!entries.iter().any(|e| e.contains("Hidden gem")),
|
||||
"Hidden gem should be behind View More"
|
||||
);
|
||||
|
||||
// User searches for the hidden thread — it appears, and View More is gone.
|
||||
type_in_search(&sidebar, "hidden gem", cx);
|
||||
let filtered = visible_entries_as_strings(&sidebar, cx);
|
||||
assert_eq!(
|
||||
filtered,
|
||||
vec![
|
||||
//
|
||||
"v [my-project]",
|
||||
" Hidden gem thread <== selected",
|
||||
]
|
||||
);
|
||||
assert!(
|
||||
!filtered.iter().any(|e| e.contains("View More")),
|
||||
"View More should not appear when filtering"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_finds_threads_inside_collapsed_groups(cx: &mut TestAppContext) {
|
||||
let project = init_test_project("/my-project", cx).await;
|
||||
|
|
@ -2341,7 +2137,6 @@ async fn test_confirm_on_historical_thread_in_new_project_group_opens_real_threa
|
|||
key: project_b_key.clone(),
|
||||
workspaces: Vec::new(),
|
||||
expanded: true,
|
||||
visible_thread_count: None,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -3942,9 +3737,6 @@ async fn test_clicking_worktree_thread_does_not_briefly_render_as_separate_proje
|
|||
title, worktree_name
|
||||
);
|
||||
}
|
||||
ListEntry::ViewMore { .. } => {
|
||||
panic!("unexpected `View More` entry while opening linked worktree thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,10 +50,6 @@ actions!(
|
|||
NextThread,
|
||||
/// Activates the previous thread in sidebar order.
|
||||
PreviousThread,
|
||||
/// Expands the thread list for the current project to show more threads.
|
||||
ShowMoreThreads,
|
||||
/// Collapses the thread list for the current project to show fewer threads.
|
||||
ShowFewerThreads,
|
||||
/// Creates a new thread in the current workspace.
|
||||
NewThread,
|
||||
/// Moves the active project to a new window.
|
||||
|
|
@ -272,20 +268,17 @@ pub struct ProjectGroup {
|
|||
pub key: ProjectGroupKey,
|
||||
pub workspaces: Vec<Entity<Workspace>>,
|
||||
pub expanded: bool,
|
||||
pub visible_thread_count: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct SerializedProjectGroupState {
|
||||
pub key: ProjectGroupKey,
|
||||
pub expanded: bool,
|
||||
pub visible_thread_count: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProjectGroupState {
|
||||
pub key: ProjectGroupKey,
|
||||
pub expanded: bool,
|
||||
pub visible_thread_count: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct MultiWorkspace {
|
||||
|
|
@ -641,7 +634,6 @@ impl MultiWorkspace {
|
|||
ProjectGroupState {
|
||||
key,
|
||||
expanded: true,
|
||||
visible_thread_count: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -757,23 +749,14 @@ impl MultiWorkspace {
|
|||
_cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut restored: Vec<ProjectGroupState> = Vec::new();
|
||||
for SerializedProjectGroupState {
|
||||
key,
|
||||
expanded,
|
||||
visible_thread_count,
|
||||
} in groups
|
||||
{
|
||||
for SerializedProjectGroupState { key, expanded } in groups {
|
||||
if key.path_list().paths().is_empty() {
|
||||
continue;
|
||||
}
|
||||
if restored.iter().any(|group| group.key == key) {
|
||||
continue;
|
||||
}
|
||||
restored.push(ProjectGroupState {
|
||||
key,
|
||||
expanded,
|
||||
visible_thread_count,
|
||||
});
|
||||
restored.push(ProjectGroupState { key, expanded });
|
||||
}
|
||||
for existing in std::mem::take(&mut self.project_groups) {
|
||||
if !restored.iter().any(|group| group.key == existing.key) {
|
||||
|
|
@ -802,7 +785,6 @@ impl MultiWorkspace {
|
|||
.cloned()
|
||||
.collect(),
|
||||
expanded: group.expanded,
|
||||
visible_thread_count: group.visible_thread_count,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -830,12 +812,6 @@ impl MultiWorkspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_all_groups_visible_thread_count(&mut self, count: Option<usize>) {
|
||||
for group in &mut self.project_groups {
|
||||
group.visible_thread_count = count;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspaces_for_project_group(
|
||||
&self,
|
||||
key: &ProjectGroupKey,
|
||||
|
|
@ -1329,7 +1305,6 @@ impl MultiWorkspace {
|
|||
crate::persistence::model::SerializedProjectGroup::from_group(
|
||||
&group.key,
|
||||
group.expanded,
|
||||
group.visible_thread_count,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
|
|
@ -1471,7 +1446,6 @@ impl MultiWorkspace {
|
|||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test_expand_all_groups(&mut self) {
|
||||
self.set_all_groups_expanded(true);
|
||||
self.set_all_groups_visible_thread_count(Some(10_000));
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
|
@ -1527,7 +1501,6 @@ impl MultiWorkspace {
|
|||
self.project_groups.push(ProjectGroupState {
|
||||
key: group.key,
|
||||
expanded: group.expanded,
|
||||
visible_thread_count: group.visible_thread_count,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,6 @@ pub struct SerializedProjectGroup {
|
|||
pub(crate) location: SerializedWorkspaceLocation,
|
||||
#[serde(default = "default_expanded")]
|
||||
pub expanded: bool,
|
||||
#[serde(default)]
|
||||
pub visible_thread_count: Option<usize>,
|
||||
}
|
||||
|
||||
fn default_expanded() -> bool {
|
||||
|
|
@ -75,11 +73,7 @@ fn default_expanded() -> bool {
|
|||
}
|
||||
|
||||
impl SerializedProjectGroup {
|
||||
pub fn from_group(
|
||||
key: &ProjectGroupKey,
|
||||
expanded: bool,
|
||||
visible_thread_count: Option<usize>,
|
||||
) -> Self {
|
||||
pub fn from_group(key: &ProjectGroupKey, expanded: bool) -> Self {
|
||||
Self {
|
||||
path_list: key.path_list().serialize(),
|
||||
location: match key.host() {
|
||||
|
|
@ -87,7 +81,6 @@ impl SerializedProjectGroup {
|
|||
None => SerializedWorkspaceLocation::Local,
|
||||
},
|
||||
expanded,
|
||||
visible_thread_count,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +93,6 @@ impl SerializedProjectGroup {
|
|||
SerializedProjectGroupState {
|
||||
key: ProjectGroupKey::new(host, path_list),
|
||||
expanded: self.expanded,
|
||||
visible_thread_count: self.visible_thread_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ pub use dock::Panel;
|
|||
pub use multi_workspace::{
|
||||
CloseWorkspaceSidebar, DraggedSidebar, FocusWorkspaceSidebar, MoveProjectToNewWindow,
|
||||
MultiWorkspace, MultiWorkspaceEvent, NewThread, NextProject, NextThread, PreviousProject,
|
||||
PreviousThread, ProjectGroup, ProjectGroupKey, SerializedProjectGroupState, ShowFewerThreads,
|
||||
ShowMoreThreads, Sidebar, SidebarEvent, SidebarHandle, SidebarRenderState, SidebarSide,
|
||||
ToggleWorkspaceSidebar, sidebar_side_context_menu,
|
||||
PreviousThread, ProjectGroup, ProjectGroupKey, SerializedProjectGroupState, Sidebar,
|
||||
SidebarEvent, SidebarHandle, SidebarRenderState, SidebarSide, ToggleWorkspaceSidebar,
|
||||
sidebar_side_context_menu,
|
||||
};
|
||||
pub use path_list::{PathList, SerializedPathList};
|
||||
pub use remote::{
|
||||
|
|
@ -8802,11 +8802,7 @@ pub async fn apply_restored_multiworkspace_state(
|
|||
// stale keys from previous sessions get normalized and deduped.
|
||||
let mut resolved_groups: Vec<SerializedProjectGroupState> = Vec::new();
|
||||
for serialized in project_groups.iter().cloned() {
|
||||
let SerializedProjectGroupState {
|
||||
key,
|
||||
expanded,
|
||||
visible_thread_count,
|
||||
} = serialized.into_restored_state();
|
||||
let SerializedProjectGroupState { key, expanded } = serialized.into_restored_state();
|
||||
if key.path_list().paths().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -8827,7 +8823,6 @@ pub async fn apply_restored_multiworkspace_state(
|
|||
resolved_groups.push(SerializedProjectGroupState {
|
||||
key: resolved,
|
||||
expanded,
|
||||
visible_thread_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue