diff --git a/Cargo.lock b/Cargo.lock index bbc88e6785..2e260f1e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3081,6 +3081,7 @@ dependencies = [ "settings2", "text2", "theme2", + "ui2", "util", "workspace2", ] diff --git a/crates/file_finder2/Cargo.toml b/crates/file_finder2/Cargo.toml index 8950cff792..22b9f2cbc8 100644 --- a/crates/file_finder2/Cargo.toml +++ b/crates/file_finder2/Cargo.toml @@ -20,6 +20,7 @@ settings = { package = "settings2", path = "../settings2" } text = { package = "text2", path = "../text2" } util = { path = "../util" } theme = { package = "theme2", path = "../theme2" } +ui = { package = "ui2", path = "../ui2" } workspace = { package = "workspace2", path = "../workspace2" } postage.workspace = true serde.workspace = true diff --git a/crates/file_finder2/src/file_finder.rs b/crates/file_finder2/src/file_finder.rs index a9b5be1dcd..67fb1e400f 100644 --- a/crates/file_finder2/src/file_finder.rs +++ b/crates/file_finder2/src/file_finder.rs @@ -1,7 +1,9 @@ use collections::HashMap; use editor::{scroll::autoscroll::Autoscroll, Bias, Editor}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; -use gpui::{actions, AppContext, Task, ViewContext, View, EventEmitter, WindowContext}; +use gpui::{ + actions, AppContext, Div, EventEmitter, Render, Task, View, ViewContext, WindowContext, +}; use picker::{Picker, PickerDelegate}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; use std::{ @@ -12,13 +14,13 @@ use std::{ }, }; use text::Point; -use util::{paths::PathLikeWithPosition, post_inc, ResultExt}; -use workspace::{Workspace, Modal, ModalEvent}; +use util::{paths::PathLikeWithPosition, post_inc}; +use workspace::{Modal, ModalEvent, Workspace}; actions!(Toggle); pub struct FileFinder { - picker: View> + picker: View>, } pub fn init(cx: &mut AppContext) { @@ -28,21 +30,88 @@ pub fn init(cx: &mut AppContext) { impl FileFinder { fn register(workspace: &mut Workspace, _: &mut ViewContext) { workspace.register_action(|workspace, _: &Toggle, cx| { - workspace.toggle_modal(cx, |cx| FileFinder::new(cx)); + let Some(file_finder) = workspace.current_modal::(cx) else { + workspace.toggle_modal(cx, |cx| FileFinder::new(workspace, cx)); + return; + }; + file_finder.update(cx, |file_finder, cx| { + file_finder + .picker + .update(cx, |picker, cx| picker.cycle_selection(cx)) + }) }); } - fn new(cx: &mut ViewContext) -> Self { - FileFinder{ + fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> Self { + let project = workspace.project().read(cx); - } + let currently_opened_path = workspace + .active_item(cx) + .and_then(|item| item.project_path(cx)) + .map(|project_path| { + let abs_path = project + .worktree_for_id(project_path.worktree_id, cx) + .map(|worktree| worktree.read(cx).abs_path().join(&project_path.path)); + FoundPath::new(project_path, abs_path) + }); + + // if exists, bubble the currently opened path to the top + let history_items = currently_opened_path + .clone() + .into_iter() + .chain( + workspace + .recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx) + .into_iter() + .filter(|(history_path, _)| { + Some(history_path) + != currently_opened_path + .as_ref() + .map(|found_path| &found_path.project) + }) + .filter(|(_, history_abs_path)| { + history_abs_path.as_ref() + != currently_opened_path + .as_ref() + .and_then(|found_path| found_path.absolute.as_ref()) + }) + .filter(|(_, history_abs_path)| match history_abs_path { + Some(abs_path) => history_file_exists(abs_path), + None => true, + }) + .map(|(history_path, abs_path)| FoundPath::new(history_path, abs_path)), + ) + .collect(); + + let project = workspace.project().clone(); + let workspace = cx.handle().downgrade(); + let finder = cx.add_view(|cx| { + Picker::new( + FileFinderDelegate::new( + workspace, + project, + currently_opened_path, + history_items, + cx, + ), + cx, + ) + }); + finder } } -impl EventEmitter for FileFinder; -impl Modal for FileFinder{ +impl EventEmitter for FileFinder {} +impl Modal for FileFinder { fn focus(&self, cx: &mut WindowContext) { - self.picker.update(cx, |picker, cx| { picker.focus(cx) }) + self.picker.update(cx, |picker, cx| picker.focus(cx)) + } +} +impl Render for FileFinder { + type Element = Div; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + v_stack().w_96().child(self.picker.clone()) } } diff --git a/crates/picker2/src/picker2.rs b/crates/picker2/src/picker2.rs index 0cfe5c8992..97f4262623 100644 --- a/crates/picker2/src/picker2.rs +++ b/crates/picker2/src/picker2.rs @@ -58,7 +58,7 @@ impl Picker { self.editor.update(cx, |editor, cx| editor.focus(cx)); } - fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext) { + pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext) { let count = self.delegate.match_count(); if count > 0 { let index = self.delegate.selected_index(); @@ -98,6 +98,15 @@ impl Picker { } } + pub fn cycle_selection(&mut self, cx: &mut ViewContext) { + let count = self.delegate.match_count(); + let index = self.delegate.selected_index(); + let new_index = if index + 1 == count { 0 } else { index + 1 }; + self.delegate.set_selected_index(new_index, cx); + self.scroll_handle.scroll_to_item(new_index); + cx.notify(); + } + fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { self.delegate.dismissed(cx); }