mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
Add tests for Pane::add_item
This commit is contained in:
parent
042ece00b1
commit
22f62ee137
2 changed files with 306 additions and 44 deletions
|
@ -449,13 +449,13 @@ impl Pane {
|
|||
// Even if the item exists, we re-add it to reorder it after the active item.
|
||||
// We may revisit this behavior after adding an "activation history" for pane items.
|
||||
let item = existing_item.unwrap_or_else(|| pane.update(cx, |_, cx| build_item(cx)));
|
||||
Pane::add_item(workspace, pane, item.clone(), true, focus_item, cx);
|
||||
Pane::add_item(workspace, &pane, item.clone(), true, focus_item, None, cx);
|
||||
item
|
||||
}
|
||||
|
||||
pub fn add_item_at(
|
||||
pub fn add_item(
|
||||
workspace: &mut Workspace,
|
||||
pane: ViewHandle<Pane>,
|
||||
pane: &ViewHandle<Pane>,
|
||||
item: Box<dyn ItemHandle>,
|
||||
activate_pane: bool,
|
||||
focus_item: bool,
|
||||
|
@ -463,69 +463,74 @@ impl Pane {
|
|||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
// If no destination index is specified, add or move the item after the active item.
|
||||
let mut destination_index = if let Some(destination_index) = destination_index {
|
||||
destination_index
|
||||
} else {
|
||||
let mut insertion_index = {
|
||||
let pane = pane.read(cx);
|
||||
cmp::min(pane.active_item_index + 1, pane.items.len())
|
||||
cmp::min(
|
||||
if let Some(destination_index) = destination_index {
|
||||
destination_index
|
||||
} else {
|
||||
pane.active_item_index + 1
|
||||
},
|
||||
pane.items.len(),
|
||||
)
|
||||
};
|
||||
|
||||
// Does the item already exist?
|
||||
if let Some(existing_item_index) = pane.read(cx).items.iter().position(|i| {
|
||||
i.id() == item.id() || i.project_entry_ids(cx) == item.project_entry_ids(cx)
|
||||
if let Some(existing_item_index) = pane.read(cx).items.iter().position(|existing_item| {
|
||||
let existing_item_entry_ids = existing_item.project_entry_ids(cx);
|
||||
let added_item_entry_ids = item.project_entry_ids(cx);
|
||||
let entries_match = !existing_item_entry_ids.is_empty()
|
||||
&& existing_item_entry_ids == added_item_entry_ids;
|
||||
|
||||
existing_item.id() == item.id() || entries_match
|
||||
}) {
|
||||
// If the item already exists, move it to the desired destination and activate it
|
||||
pane.update(cx, |pane, cx| {
|
||||
if existing_item_index != destination_index {
|
||||
if existing_item_index != insertion_index {
|
||||
cx.reparent(&item);
|
||||
let existing_item_is_active = existing_item_index == pane.active_item_index;
|
||||
|
||||
pane.items.remove(existing_item_index);
|
||||
if existing_item_index < pane.active_item_index {
|
||||
pane.active_item_index -= 1;
|
||||
}
|
||||
destination_index = destination_index.min(pane.items.len());
|
||||
// If the caller didn't specify a destination and the added item is already
|
||||
// the active one, don't move it
|
||||
if existing_item_is_active && destination_index.is_none() {
|
||||
insertion_index = existing_item_index;
|
||||
} else {
|
||||
pane.items.remove(existing_item_index);
|
||||
if existing_item_index < pane.active_item_index {
|
||||
pane.active_item_index -= 1;
|
||||
}
|
||||
insertion_index = insertion_index.min(pane.items.len());
|
||||
|
||||
pane.items.insert(destination_index, item.clone());
|
||||
pane.items.insert(insertion_index, item.clone());
|
||||
|
||||
if existing_item_is_active {
|
||||
pane.active_item_index = destination_index;
|
||||
} else if destination_index <= pane.active_item_index {
|
||||
pane.active_item_index += 1;
|
||||
if existing_item_is_active {
|
||||
pane.active_item_index = insertion_index;
|
||||
} else if insertion_index <= pane.active_item_index {
|
||||
pane.active_item_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pane.activate_item(destination_index, activate_pane, focus_item, cx);
|
||||
pane.activate_item(insertion_index, activate_pane, focus_item, cx);
|
||||
});
|
||||
} else {
|
||||
// If the item doesn't already exist, add it and activate it
|
||||
item.added_to_pane(workspace, pane.clone(), cx);
|
||||
pane.update(cx, |pane, cx| {
|
||||
cx.reparent(&item);
|
||||
pane.items.insert(destination_index, item);
|
||||
if destination_index <= pane.active_item_index {
|
||||
pane.items.insert(insertion_index, item);
|
||||
if insertion_index <= pane.active_item_index {
|
||||
pane.active_item_index += 1;
|
||||
}
|
||||
|
||||
pane.activate_item(destination_index, activate_pane, focus_item, cx);
|
||||
pane.activate_item(insertion_index, activate_pane, focus_item, cx);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_item(
|
||||
workspace: &mut Workspace,
|
||||
pane: ViewHandle<Pane>,
|
||||
item: Box<dyn ItemHandle>,
|
||||
activate_pane: bool,
|
||||
focus_item: bool,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
Self::add_item_at(workspace, pane, item, activate_pane, focus_item, None, cx)
|
||||
}
|
||||
|
||||
pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
@ -913,9 +918,9 @@ impl Pane {
|
|||
.expect("Tried to move item handle which was not in from pane");
|
||||
|
||||
// This automatically removes duplicate items in the pane
|
||||
Pane::add_item_at(
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
to.clone(),
|
||||
&to,
|
||||
item_handle.clone(),
|
||||
true,
|
||||
true,
|
||||
|
@ -1527,3 +1532,252 @@ impl NavHistory {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use project::FakeFs;
|
||||
|
||||
use crate::tests::TestItem;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
Settings::test_async(cx);
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
// a. Add before the active item
|
||||
set_labeled_items(&workspace, &pane, ["A", "B*", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
&pane,
|
||||
Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
|
||||
false,
|
||||
false,
|
||||
Some(0),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
|
||||
|
||||
// b. Add after the active item
|
||||
set_labeled_items(&workspace, &pane, ["A", "B*", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
&pane,
|
||||
Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
|
||||
false,
|
||||
false,
|
||||
Some(2),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
|
||||
|
||||
// c. Add at the end of the item list (including off the length)
|
||||
set_labeled_items(&workspace, &pane, ["A", "B*", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
&pane,
|
||||
Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
|
||||
false,
|
||||
false,
|
||||
Some(5),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
|
||||
|
||||
// 2. Add without a destination index
|
||||
// a. Add with active item at the start of the item list
|
||||
set_labeled_items(&workspace, &pane, ["A*", "B", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
&pane,
|
||||
Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
set_labeled_items(&workspace, &pane, ["A", "D*", "B", "C"], cx);
|
||||
|
||||
// b. Add with active item at the end of the item list
|
||||
set_labeled_items(&workspace, &pane, ["A", "B", "C*"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
&pane,
|
||||
Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
Settings::test_async(cx);
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
// 1a. Add before the active item
|
||||
let [_, _, _, d] = set_labeled_items(&workspace, &pane, ["A", "B*", "C", "D"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, d, false, false, Some(0), cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
|
||||
|
||||
// 1b. Add after the active item
|
||||
let [_, _, _, d] = set_labeled_items(&workspace, &pane, ["A", "B*", "C", "D"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, d, false, false, Some(2), cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
|
||||
|
||||
// 1c. Add at the end of the item list (including off the length)
|
||||
let [a, _, _, _] = set_labeled_items(&workspace, &pane, ["A", "B*", "C", "D"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, a, false, false, Some(5), cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
|
||||
|
||||
// 1d. Add same item to active index
|
||||
let [_, b, _] = set_labeled_items(&workspace, &pane, ["A", "B*", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, b, false, false, Some(1), cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B*", "C"], cx);
|
||||
|
||||
// 1e. Add item to index after same item in last position
|
||||
let [_, _, c] = set_labeled_items(&workspace, &pane, ["A", "B*", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, c, false, false, Some(2), cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "C*"], cx);
|
||||
|
||||
// 2. Add without a destination index
|
||||
// 2a. Add with active item at the start of the item list
|
||||
let [_, _, _, d] = set_labeled_items(&workspace, &pane, ["A*", "B", "C", "D"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, d, false, false, None, cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "D*", "B", "C"], cx);
|
||||
|
||||
// 2b. Add with active item at the end of the item list
|
||||
let [a, _, _, _] = set_labeled_items(&workspace, &pane, ["A", "B", "C", "D*"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, a, false, false, None, cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
|
||||
|
||||
// 2c. Add active item to active item at end of list
|
||||
let [_, _, c] = set_labeled_items(&workspace, &pane, ["A", "B", "C*"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, c, false, false, None, cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A", "B", "C*"], cx);
|
||||
|
||||
// 2d. Add active item to active item at start of list
|
||||
let [a, _, _] = set_labeled_items(&workspace, &pane, ["A*", "B", "C"], cx);
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Pane::add_item(workspace, &pane, a, false, false, None, cx);
|
||||
});
|
||||
assert_item_labels(&pane, ["A*", "B", "C"], cx);
|
||||
}
|
||||
|
||||
fn set_labeled_items<const COUNT: usize>(
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
pane: &ViewHandle<Pane>,
|
||||
labels: [&str; COUNT],
|
||||
cx: &mut TestAppContext,
|
||||
) -> [Box<ViewHandle<TestItem>>; COUNT] {
|
||||
pane.update(cx, |pane, _| {
|
||||
pane.items.clear();
|
||||
});
|
||||
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let mut active_item_index = 0;
|
||||
|
||||
let mut index = 0;
|
||||
let items = labels.map(|mut label| {
|
||||
if label.ends_with("*") {
|
||||
label = label.trim_end_matches("*");
|
||||
active_item_index = index;
|
||||
}
|
||||
|
||||
let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label)));
|
||||
Pane::add_item(
|
||||
workspace,
|
||||
pane,
|
||||
labeled_item.clone(),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
index += 1;
|
||||
labeled_item
|
||||
});
|
||||
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.activate_item(active_item_index, false, false, cx)
|
||||
});
|
||||
|
||||
items
|
||||
})
|
||||
}
|
||||
|
||||
// Assert the item label, with the active item label suffixed with a '*'
|
||||
fn assert_item_labels<const COUNT: usize>(
|
||||
pane: &ViewHandle<Pane>,
|
||||
expected_states: [&str; COUNT],
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
pane.read_with(cx, |pane, cx| {
|
||||
let actual_states = pane
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ix, item)| {
|
||||
let mut state = item
|
||||
.to_any()
|
||||
.downcast::<TestItem>()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.label
|
||||
.clone();
|
||||
if ix == pane.active_item_index {
|
||||
state.push('*');
|
||||
}
|
||||
state
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
actual_states, expected_states,
|
||||
"pane items do not match expectation"
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1448,8 +1448,8 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||
let pane = self.active_pane().clone();
|
||||
Pane::add_item(self, pane, item, true, true, cx);
|
||||
let active_pane = self.active_pane().clone();
|
||||
Pane::add_item(self, &active_pane, item, true, true, None, cx);
|
||||
}
|
||||
|
||||
pub fn open_path(
|
||||
|
@ -1649,7 +1649,7 @@ impl Workspace {
|
|||
pane.read(cx).active_item().map(|item| {
|
||||
let new_pane = self.add_pane(cx);
|
||||
if let Some(clone) = item.clone_on_split(cx.as_mut()) {
|
||||
Pane::add_item(self, new_pane.clone(), clone, true, true, cx);
|
||||
Pane::add_item(self, &new_pane, clone, true, true, None, cx);
|
||||
}
|
||||
self.center.split(&pane, &new_pane, direction).unwrap();
|
||||
cx.notify();
|
||||
|
@ -2392,7 +2392,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
for (pane, item) in items_to_add {
|
||||
Pane::add_item(self, pane.clone(), item.boxed_clone(), false, false, cx);
|
||||
Pane::add_item(self, &pane, item.boxed_clone(), false, false, None, cx);
|
||||
if pane == self.active_pane {
|
||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||
}
|
||||
|
@ -3330,8 +3330,9 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
struct TestItem {
|
||||
pub struct TestItem {
|
||||
state: String,
|
||||
pub label: String,
|
||||
save_count: usize,
|
||||
save_as_count: usize,
|
||||
reload_count: usize,
|
||||
|
@ -3345,7 +3346,7 @@ mod tests {
|
|||
tab_detail: Cell<Option<usize>>,
|
||||
}
|
||||
|
||||
enum TestItemEvent {
|
||||
pub enum TestItemEvent {
|
||||
Edit,
|
||||
}
|
||||
|
||||
|
@ -3353,6 +3354,7 @@ mod tests {
|
|||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
state: self.state.clone(),
|
||||
label: self.label.clone(),
|
||||
save_count: self.save_count,
|
||||
save_as_count: self.save_as_count,
|
||||
reload_count: self.reload_count,
|
||||
|
@ -3369,9 +3371,10 @@ mod tests {
|
|||
}
|
||||
|
||||
impl TestItem {
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: String::new(),
|
||||
label: String::new(),
|
||||
save_count: 0,
|
||||
save_as_count: 0,
|
||||
reload_count: 0,
|
||||
|
@ -3386,6 +3389,11 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_label(mut self, state: &str) -> Self {
|
||||
self.label = state.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: String, cx: &mut ViewContext<Self>) {
|
||||
self.push_to_nav_history(cx);
|
||||
self.state = state;
|
||||
|
|
Loading…
Reference in a new issue