project panel: Mark entries when opening in project panel (#20412)
This addresses #17746 by marking entries when they're opened in the project panel. I think that was the original intention behind the code too, because it explicitly marks entries before opening them. An event that is emitted by the workspace reset the mark though. So what I did was try to emulate the logic I saw in VS Code: when opening the file, mark it, when the active entry changes, unmark it, except if you explicitly marked a group of files. Closes #17746 Release Notes: - Changed project panel to mark files when opening them, which should make it more intuitive to mark multiple files after opening a single one.
This commit is contained in:
parent
706c385c24
commit
01e12c0d3c
1 changed files with 123 additions and 92 deletions
|
@ -212,7 +212,6 @@ pub enum Event {
|
|||
entry_id: ProjectEntryId,
|
||||
focus_opened_item: bool,
|
||||
allow_preview: bool,
|
||||
mark_selected: bool,
|
||||
},
|
||||
SplitEntry {
|
||||
entry_id: ProjectEntryId,
|
||||
|
@ -352,24 +351,12 @@ impl ProjectPanel {
|
|||
entry_id,
|
||||
focus_opened_item,
|
||||
allow_preview,
|
||||
mark_selected
|
||||
} => {
|
||||
if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
|
||||
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
|
||||
let file_path = entry.path.clone();
|
||||
let worktree_id = worktree.read(cx).id();
|
||||
let entry_id = entry.id;
|
||||
|
||||
project_panel.update(cx, |this, _| {
|
||||
if !mark_selected {
|
||||
this.marked_entries.clear();
|
||||
}
|
||||
this.marked_entries.insert(SelectedEntry {
|
||||
worktree_id,
|
||||
entry_id
|
||||
});
|
||||
}).ok();
|
||||
|
||||
let is_via_ssh = project.read(cx).is_via_ssh();
|
||||
|
||||
workspace
|
||||
|
@ -399,12 +386,12 @@ impl ProjectPanel {
|
|||
});
|
||||
|
||||
if let Some(project_panel) = project_panel.upgrade() {
|
||||
// Always select the entry, regardless of whether it is opened or not.
|
||||
// Always select and mark the entry, regardless of whether it is opened or not.
|
||||
project_panel.update(cx, |project_panel, _| {
|
||||
project_panel.selection = Some(SelectedEntry {
|
||||
worktree_id,
|
||||
entry_id
|
||||
});
|
||||
let entry = SelectedEntry { worktree_id, entry_id };
|
||||
project_panel.marked_entries.clear();
|
||||
project_panel.marked_entries.insert(entry);
|
||||
project_panel.selection = Some(entry);
|
||||
});
|
||||
if !focus_opened_item {
|
||||
let focus_handle = project_panel.read(cx).focus_handle.clone();
|
||||
|
@ -793,29 +780,22 @@ impl ProjectPanel {
|
|||
|
||||
fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) {
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
|
||||
self.open_internal(false, true, !preview_tabs_enabled, cx);
|
||||
self.open_internal(true, !preview_tabs_enabled, cx);
|
||||
}
|
||||
|
||||
fn open_permanent(&mut self, _: &OpenPermanent, cx: &mut ViewContext<Self>) {
|
||||
self.open_internal(true, false, true, cx);
|
||||
self.open_internal(false, true, cx);
|
||||
}
|
||||
|
||||
fn open_internal(
|
||||
&mut self,
|
||||
mark_selected: bool,
|
||||
allow_preview: bool,
|
||||
focus_opened_item: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let Some((_, entry)) = self.selected_entry(cx) {
|
||||
if entry.is_file() {
|
||||
self.open_entry(
|
||||
entry.id,
|
||||
mark_selected,
|
||||
focus_opened_item,
|
||||
allow_preview,
|
||||
cx,
|
||||
);
|
||||
self.open_entry(entry.id, focus_opened_item, allow_preview, cx);
|
||||
} else {
|
||||
self.toggle_expanded(entry.id, cx);
|
||||
}
|
||||
|
@ -897,7 +877,7 @@ impl ProjectPanel {
|
|||
}
|
||||
project_panel.update_visible_entries(None, cx);
|
||||
if is_new_entry && !is_dir {
|
||||
project_panel.open_entry(new_entry.id, false, true, false, cx);
|
||||
project_panel.open_entry(new_entry.id, true, false, cx);
|
||||
}
|
||||
cx.notify();
|
||||
})?;
|
||||
|
@ -955,7 +935,6 @@ impl ProjectPanel {
|
|||
fn open_entry(
|
||||
&mut self,
|
||||
entry_id: ProjectEntryId,
|
||||
mark_selected: bool,
|
||||
focus_opened_item: bool,
|
||||
allow_preview: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
|
@ -964,7 +943,6 @@ impl ProjectPanel {
|
|||
entry_id,
|
||||
focus_opened_item,
|
||||
allow_preview,
|
||||
mark_selected,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2172,7 +2150,7 @@ impl ProjectPanel {
|
|||
let opened_entries = task.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if open_file_after_drop && !opened_entries.is_empty() {
|
||||
this.open_entry(opened_entries[0], true, true, false, cx);
|
||||
this.open_entry(opened_entries[0], true, false, cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2605,70 +2583,60 @@ impl ProjectPanel {
|
|||
this.drag_onto(selections, entry_id, kind.is_file(), cx);
|
||||
}))
|
||||
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
|
||||
if event.down.button == MouseButton::Right || event.down.first_mouse {
|
||||
if event.down.button == MouseButton::Right || event.down.first_mouse || show_editor
|
||||
{
|
||||
return;
|
||||
}
|
||||
if !show_editor {
|
||||
cx.stop_propagation();
|
||||
|
||||
if let Some(selection) = this.selection.filter(|_| event.down.modifiers.shift) {
|
||||
let current_selection = this.index_for_selection(selection);
|
||||
let target_selection = this.index_for_selection(SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id,
|
||||
});
|
||||
if let Some(((_, _, source_index), (_, _, target_index))) =
|
||||
current_selection.zip(target_selection)
|
||||
{
|
||||
let range_start = source_index.min(target_index);
|
||||
let range_end = source_index.max(target_index) + 1; // Make the range inclusive.
|
||||
let mut new_selections = BTreeSet::new();
|
||||
this.for_each_visible_entry(
|
||||
range_start..range_end,
|
||||
cx,
|
||||
|entry_id, details, _| {
|
||||
new_selections.insert(SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id: details.worktree_id,
|
||||
});
|
||||
},
|
||||
);
|
||||
cx.stop_propagation();
|
||||
|
||||
this.marked_entries = this
|
||||
.marked_entries
|
||||
.union(&new_selections)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
this.selection = Some(SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id,
|
||||
});
|
||||
// Ensure that the current entry is selected.
|
||||
this.marked_entries.insert(SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id,
|
||||
});
|
||||
}
|
||||
} else if event.down.modifiers.secondary() {
|
||||
if event.down.click_count > 1 {
|
||||
this.split_entry(entry_id, cx);
|
||||
} else if !this.marked_entries.insert(selection) {
|
||||
this.marked_entries.remove(&selection);
|
||||
}
|
||||
} else if kind.is_dir() {
|
||||
this.toggle_expanded(entry_id, cx);
|
||||
} else {
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
|
||||
let click_count = event.up.click_count;
|
||||
this.open_entry(
|
||||
entry_id,
|
||||
cx.modifiers().secondary(),
|
||||
!preview_tabs_enabled || click_count > 1,
|
||||
preview_tabs_enabled && click_count == 1,
|
||||
if let Some(selection) = this.selection.filter(|_| event.down.modifiers.shift) {
|
||||
let current_selection = this.index_for_selection(selection);
|
||||
let clicked_entry = SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id,
|
||||
};
|
||||
let target_selection = this.index_for_selection(clicked_entry);
|
||||
if let Some(((_, _, source_index), (_, _, target_index))) =
|
||||
current_selection.zip(target_selection)
|
||||
{
|
||||
let range_start = source_index.min(target_index);
|
||||
let range_end = source_index.max(target_index) + 1; // Make the range inclusive.
|
||||
let mut new_selections = BTreeSet::new();
|
||||
this.for_each_visible_entry(
|
||||
range_start..range_end,
|
||||
cx,
|
||||
|entry_id, details, _| {
|
||||
new_selections.insert(SelectedEntry {
|
||||
entry_id,
|
||||
worktree_id: details.worktree_id,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
this.marked_entries = this
|
||||
.marked_entries
|
||||
.union(&new_selections)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
this.selection = Some(clicked_entry);
|
||||
this.marked_entries.insert(clicked_entry);
|
||||
}
|
||||
} else if event.down.modifiers.secondary() {
|
||||
if event.down.click_count > 1 {
|
||||
this.split_entry(entry_id, cx);
|
||||
} else if !this.marked_entries.insert(selection) {
|
||||
this.marked_entries.remove(&selection);
|
||||
}
|
||||
} else if kind.is_dir() {
|
||||
this.toggle_expanded(entry_id, cx);
|
||||
} else {
|
||||
let preview_tabs_enabled = PreviewTabsSettings::get_global(cx).enabled;
|
||||
let click_count = event.up.click_count;
|
||||
let focus_opened_item = !preview_tabs_enabled || click_count > 1;
|
||||
let allow_preview = preview_tabs_enabled && click_count == 1;
|
||||
this.open_entry(entry_id, focus_opened_item, allow_preview, cx);
|
||||
}
|
||||
}))
|
||||
.cursor_pointer()
|
||||
|
@ -2996,9 +2964,18 @@ impl ProjectPanel {
|
|||
}
|
||||
|
||||
let worktree_id = worktree.id();
|
||||
self.marked_entries.clear();
|
||||
self.expand_entry(worktree_id, entry_id, cx);
|
||||
self.update_visible_entries(Some((worktree_id, entry_id)), cx);
|
||||
|
||||
if self.marked_entries.len() == 1
|
||||
&& self
|
||||
.marked_entries
|
||||
.first()
|
||||
.filter(|entry| entry.entry_id == entry_id)
|
||||
.is_none()
|
||||
{
|
||||
self.marked_entries.clear();
|
||||
}
|
||||
self.autoscroll(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -3629,6 +3606,60 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_opening_file(cx: &mut gpui::TestAppContext) {
|
||||
init_test_with_editor(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
fs.insert_tree(
|
||||
"/src",
|
||||
json!({
|
||||
"test": {
|
||||
"first.rs": "// First Rust file",
|
||||
"second.rs": "// Second Rust file",
|
||||
"third.rs": "// Third Rust file",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
let panel = workspace.update(cx, ProjectPanel::new).unwrap();
|
||||
|
||||
toggle_expand_dir(&panel, "src/test", cx);
|
||||
select_path(&panel, "src/test/first.rs", cx);
|
||||
panel.update(cx, |panel, cx| panel.open(&Open, cx));
|
||||
cx.executor().run_until_parked();
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&[
|
||||
"v src",
|
||||
" v test",
|
||||
" first.rs <== selected <== marked",
|
||||
" second.rs",
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(&workspace, "test/first.rs", cx);
|
||||
|
||||
select_path(&panel, "src/test/second.rs", cx);
|
||||
panel.update(cx, |panel, cx| panel.open(&Open, cx));
|
||||
cx.executor().run_until_parked();
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&[
|
||||
"v src",
|
||||
" v test",
|
||||
" first.rs",
|
||||
" second.rs <== selected <== marked",
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(&workspace, "test/second.rs", cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_exclusions_in_visible_list(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
@ -4853,7 +4884,7 @@ mod tests {
|
|||
&[
|
||||
"v src",
|
||||
" v test",
|
||||
" first.rs <== selected",
|
||||
" first.rs <== selected <== marked",
|
||||
" second.rs",
|
||||
" third.rs"
|
||||
]
|
||||
|
@ -4881,7 +4912,7 @@ mod tests {
|
|||
&[
|
||||
"v src",
|
||||
" v test",
|
||||
" second.rs <== selected",
|
||||
" second.rs <== selected <== marked",
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue