Skip to content

Commit

Permalink
Add a way to use splits when opening in file finder (zed-industries#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeoneToIgnore authored Nov 13, 2024
1 parent 90ffd65 commit 56cf32c
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 16 deletions.
14 changes: 13 additions & 1 deletion assets/keymaps/default-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,19 @@
},
{
"context": "FileFinder",
"bindings": { "ctrl-shift-p": "file_finder::SelectPrev" }
"bindings": {
"ctrl-shift-p": "file_finder::SelectPrev",
"ctrl-k": "file_finder::OpenMenu"
}
},
{
"context": "FileFinder && menu_open",
"bindings": {
"u": "pane::SplitUp",
"d": "pane::SplitDown",
"l": "pane::SplitLeft",
"r": "pane::SplitRight"
}
},
{
"context": "TabSwitcher",
Expand Down
14 changes: 13 additions & 1 deletion assets/keymaps/default-macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,19 @@
},
{
"context": "FileFinder",
"bindings": { "cmd-shift-p": "file_finder::SelectPrev" }
"bindings": {
"cmd-shift-p": "file_finder::SelectPrev",
"cmd-k": "file_finder::OpenMenu"
}
},
{
"context": "FileFinder && menu_open",
"bindings": {
"u": "pane::SplitUp",
"d": "pane::SplitDown",
"l": "pane::SplitLeft",
"r": "pane::SplitRight"
}
},
{
"context": "TabSwitcher",
Expand Down
171 changes: 160 additions & 11 deletions crates/file_finder/src/file_finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use file_icons::FileIcons;
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{
actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
FocusableView, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task,
View, ViewContext, VisualContext, WeakView,
FocusableView, KeyContext, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
Styled, Task, View, ViewContext, VisualContext, WeakView,
};
use new_path_prompt::NewPathPrompt;
use open_path_prompt::OpenPathPrompt;
Expand All @@ -32,16 +32,30 @@ use std::{
},
};
use text::Point;
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use ui::{
prelude::*, ButtonLike, ContextMenu, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing,
PopoverMenu, PopoverMenuHandle, TintColor,
};
use util::{paths::PathWithPosition, post_inc, ResultExt};
use workspace::{item::PreviewTabsSettings, notifications::NotifyResultExt, ModalView, Workspace};
use workspace::{
item::PreviewTabsSettings, notifications::NotifyResultExt, pane, ModalView, SplitDirection,
Workspace,
};

actions!(file_finder, [SelectPrev]);
actions!(file_finder, [SelectPrev, OpenMenu]);

impl ModalView for FileFinder {}
impl ModalView for FileFinder {
fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> workspace::DismissDecision {
let submenu_focused = self.picker.update(cx, |picker, cx| {
picker.delegate.popover_menu_handle.is_focused(cx)
});
workspace::DismissDecision::Dismiss(!submenu_focused)
}
}

pub struct FileFinder {
picker: View<Picker<FileFinderDelegate>>,
picker_focus_handle: FocusHandle,
init_modifiers: Option<Modifiers>,
}

Expand Down Expand Up @@ -142,8 +156,14 @@ impl FileFinder {
}

fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
let picker_focus_handle = picker.focus_handle(cx);
picker.update(cx, |picker, _| {
picker.delegate.focus_handle = picker_focus_handle.clone();
});
Self {
picker: cx.new_view(|cx| Picker::uniform_list(delegate, cx)),
picker,
picker_focus_handle,
init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
}
}
Expand All @@ -168,23 +188,85 @@ impl FileFinder {
self.init_modifiers = Some(cx.modifiers());
cx.dispatch_action(Box::new(menu::SelectPrev));
}

fn handle_open_menu(&mut self, _: &OpenMenu, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| {
let menu_handle = &picker.delegate.popover_menu_handle;
if !menu_handle.is_deployed() {
menu_handle.show(cx);
}
});
}

fn go_to_file_split_left(&mut self, _: &pane::SplitLeft, cx: &mut ViewContext<Self>) {
self.go_to_file_split_inner(SplitDirection::Left, cx)
}

fn go_to_file_split_right(&mut self, _: &pane::SplitRight, cx: &mut ViewContext<Self>) {
self.go_to_file_split_inner(SplitDirection::Right, cx)
}

fn go_to_file_split_up(&mut self, _: &pane::SplitUp, cx: &mut ViewContext<Self>) {
self.go_to_file_split_inner(SplitDirection::Up, cx)
}

fn go_to_file_split_down(&mut self, _: &pane::SplitDown, cx: &mut ViewContext<Self>) {
self.go_to_file_split_inner(SplitDirection::Down, cx)
}

fn go_to_file_split_inner(
&mut self,
split_direction: SplitDirection,
cx: &mut ViewContext<Self>,
) {
self.picker.update(cx, |picker, cx| {
let delegate = &mut picker.delegate;
if let Some(workspace) = delegate.workspace.upgrade() {
if let Some(m) = delegate.matches.get(delegate.selected_index()) {
let path = match &m {
Match::History { path, .. } => {
let worktree_id = path.project.worktree_id;
ProjectPath {
worktree_id,
path: Arc::clone(&path.project.path),
}
}
Match::Search(m) => ProjectPath {
worktree_id: WorktreeId::from_usize(m.0.worktree_id),
path: m.0.path.clone(),
},
};
let open_task = workspace.update(cx, move |workspace, cx| {
workspace.split_path_preview(path, false, Some(split_direction), cx)
});
open_task.detach_and_log_err(cx);
}
}
})
}
}

impl EventEmitter<DismissEvent> for FileFinder {}

impl FocusableView for FileFinder {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.picker.focus_handle(cx)
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.picker_focus_handle.clone()
}
}

impl Render for FileFinder {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let key_context = self.picker.read(cx).delegate.key_context(cx);
v_flex()
.key_context("FileFinder")
.key_context(key_context)
.w(rems(34.))
.on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
.on_action(cx.listener(Self::handle_select_prev))
.on_action(cx.listener(Self::handle_open_menu))
.on_action(cx.listener(Self::go_to_file_split_left))
.on_action(cx.listener(Self::go_to_file_split_right))
.on_action(cx.listener(Self::go_to_file_split_up))
.on_action(cx.listener(Self::go_to_file_split_down))
.child(self.picker.clone())
}
}
Expand All @@ -205,6 +287,8 @@ pub struct FileFinderDelegate {
history_items: Vec<FoundPath>,
separate_history: bool,
first_update: bool,
popover_menu_handle: PopoverMenuHandle<ContextMenu>,
focus_handle: FocusHandle,
}

/// Use a custom ordering for file finder: the regular one
Expand Down Expand Up @@ -533,6 +617,8 @@ impl FileFinderDelegate {
history_items,
separate_history,
first_update: true,
popover_menu_handle: PopoverMenuHandle::default(),
focus_handle: cx.focus_handle(),
}
}

Expand Down Expand Up @@ -845,6 +931,15 @@ impl FileFinderDelegate {

0
}

fn key_context(&self, cx: &WindowContext) -> KeyContext {
let mut key_context = KeyContext::new_with_defaults();
key_context.add("FileFinder");
if self.popover_menu_handle.is_focused(cx) {
key_context.add("menu_open");
}
key_context
}
}

impl PickerDelegate for FileFinderDelegate {
Expand Down Expand Up @@ -958,7 +1053,7 @@ impl PickerDelegate for FileFinderDelegate {
let allow_preview =
PreviewTabsSettings::get_global(cx).enable_preview_from_file_finder;
if secondary {
workspace.split_path_preview(project_path, allow_preview, cx)
workspace.split_path_preview(project_path, allow_preview, None, cx)
} else {
workspace.open_path_preview(
project_path,
Expand Down Expand Up @@ -1125,6 +1220,60 @@ impl PickerDelegate for FileFinderDelegate {
),
)
}

fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
let menu_open = self.popover_menu_handle.is_focused(cx);
Some(
h_flex()
.w_full()
.border_t_1()
.py_2()
.pr_2()
.border_color(cx.theme().colors().border)
.justify_end()
.child(
ButtonLike::new("open-selection")
.when_some(KeyBinding::for_action(&menu::Confirm, cx), |button, key| {
button.child(key)
})
.child(Label::new("Open"))
.on_click(|_, cx| cx.dispatch_action(menu::Confirm.boxed_clone())),
)
.child(
div().pl_2().child(
PopoverMenu::new("menu-popover")
.with_handle(self.popover_menu_handle.clone())
.attach(gpui::AnchorCorner::TopRight)
.anchor(gpui::AnchorCorner::BottomRight)
.trigger(
ButtonLike::new("menu-trigger")
.selected(menu_open)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.when_some(
KeyBinding::for_action_in(
&OpenMenu,
&self.focus_handle,
cx,
),
|button, key| button.child(key),
)
.child(Label::new("More actions")),
)
.menu({
move |cx| {
Some(ContextMenu::build(cx, move |menu, _| {
menu.action("Split left", pane::SplitLeft.boxed_clone())
.action("Split right", pane::SplitRight.boxed_clone())
.action("Split up", pane::SplitUp.boxed_clone())
.action("Split down", pane::SplitDown.boxed_clone())
}))
}
}),
),
)
.into_any(),
)
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/ui/src/components/popover_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
.map_or(false, |state| state.menu.borrow().as_ref().is_some())
}

pub fn is_focused(&self, cx: &mut WindowContext) -> bool {
pub fn is_focused(&self, cx: &WindowContext) -> bool {
self.0.borrow().as_ref().map_or(false, |state| {
state
.menu
Expand Down
6 changes: 4 additions & 2 deletions crates/workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2631,13 +2631,14 @@ impl Workspace {
path: impl Into<ProjectPath>,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
self.split_path_preview(path, false, cx)
self.split_path_preview(path, false, None, cx)
}

pub fn split_path_preview(
&mut self,
path: impl Into<ProjectPath>,
allow_preview: bool,
split_direction: Option<SplitDirection>,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
Expand All @@ -2658,7 +2659,8 @@ impl Workspace {
let (project_entry_id, build_item) = task.await?;
this.update(&mut cx, move |this, cx| -> Option<_> {
let pane = pane.upgrade()?;
let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
let new_pane =
this.split_pane(pane, split_direction.unwrap_or(SplitDirection::Right), cx);
new_pane.update(cx, |new_pane, cx| {
Some(new_pane.open_item(project_entry_id, true, allow_preview, cx, build_item))
})
Expand Down

0 comments on commit 56cf32c

Please sign in to comment.