Compare commits

...

1 Commits

Author SHA1 Message Date
Nate Butler
5f24fe5b5b wip 2025-01-15 11:59:27 -05:00
6 changed files with 188 additions and 1 deletions

1
Cargo.lock generated
View File

@@ -5121,6 +5121,7 @@ dependencies = [
"gpui",
"language",
"menu",
"picker",
"project",
"schemars",
"serde",

View File

@@ -34,6 +34,7 @@ ui.workspace = true
util.workspace = true
workspace.workspace = true
worktree.workspace = true
picker.workspace = true
[target.'cfg(windows)'.dependencies]
windows.workspace = true

View File

@@ -1,4 +1,5 @@
use crate::git_panel_settings::StatusStyle;
use crate::repo_selector::{RepoSelector, RepoSelectorTrigger};
use crate::{first_repository_in_project, first_worktree_repository};
use crate::{
git_panel_settings::GitPanelSettings, git_status_icon, CommitAllChanges, CommitChanges,
@@ -89,6 +90,7 @@ pub struct GitPanel {
show_scrollbar: bool,
rebuild_requested: Arc<AtomicBool>,
git_state: Model<GitState>,
repo_selector: View<RepoSelector>,
commit_editor: View<Editor>,
/// The visible entries in the list, accounting for folding & expanded state.
///
@@ -117,6 +119,8 @@ impl GitPanel {
let state = git_state.read(cx);
state.commit_message.clone()
};
let repo_selector =
cx.new_view(|cx| RepoSelector::new(project.clone(), git_state.clone(), cx));
let git_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
let focus_handle = cx.focus_handle();
@@ -321,6 +325,7 @@ impl GitPanel {
hide_scrollbar_task: None,
rebuild_requested,
commit_editor,
repo_selector,
git_state,
reveal_in_editor: Task::ready(()),
project,
@@ -1293,6 +1298,12 @@ impl Render for GitPanel {
} else {
self.render_empty_state(cx).into_any_element()
})
.child(
h_flex()
.w_full()
.flex_none()
.child(RepoSelectorTrigger::new(self.git_state.clone())),
)
.child(self.render_divider(cx))
.child(self.render_commit_editor(cx))
}

View File

@@ -14,6 +14,7 @@ use worktree::RepositoryEntry;
pub mod git_panel;
mod git_panel_settings;
mod repo_selector;
actions!(
git,

View File

@@ -0,0 +1,173 @@
use crate::GitState;
use gpui::*;
use picker::{Picker, PickerDelegate};
use project::Project;
use std::sync::Arc;
use ui::{prelude::*, Button, ListItem, ListItemSpacing, Tooltip};
actions!(repo_selector, [Confirm]);
pub struct RepoSelector {
picker: View<Picker<RepoSelectorDelegate>>,
project: Model<Project>,
git_state: Model<GitState>,
}
impl RepoSelector {
pub fn new(
project: Model<Project>,
git_state: Model<GitState>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate = RepoSelectorDelegate {
project: project.clone(),
git_state: git_state.clone(),
repositories: Vec::new(),
selected_index: 0,
};
let picker =
cx.new_view(|cx| Picker::uniform_list(delegate, cx).max_height(Some(rems(20.).into())));
Self {
picker,
project,
git_state,
}
}
}
impl FocusableView for RepoSelector {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl EventEmitter<DismissEvent> for RepoSelector {}
impl Render for RepoSelector {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div().size_full().child(self.picker.clone())
}
}
pub struct RepoSelectorDelegate {
project: Model<Project>,
git_state: Model<GitState>,
repositories: Vec<SharedString>,
selected_index: usize,
}
impl PickerDelegate for RepoSelectorDelegate {
type ListItem = ListItem;
fn match_count(&self) -> usize {
self.repositories.len()
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
let worktree = self.project.read(cx).worktrees(cx).nth(ix);
if let Some(worktree) = worktree.clone() {
let worktree_id = worktree.read(cx).id();
if let Some((repo, git_repo)) =
crate::first_worktree_repository(&self.project, worktree_id, cx)
{
self.git_state.update(cx, |state, _| {
state.activate_repository(worktree_id, repo, git_repo);
});
}
}
}
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
"Select a repository...".into()
}
fn render_match(
&self,
ix: usize,
selected: bool,
cx: &mut ViewContext<Picker<Self>>,
) -> Option<Self::ListItem> {
self.project.read(cx).worktrees(cx).nth(ix).map(|worktree| {
let root_name = worktree.read(cx).root_name().to_string();
ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.child(root_name)
})
}
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
cx.emit(DismissEvent);
}
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
Task::ready(())
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
cx.emit(DismissEvent);
}
}
#[derive(IntoElement)]
pub struct RepoSelectorTrigger {
git_state: Model<GitState>,
cursor_style: CursorStyle,
selected: bool,
}
impl RepoSelectorTrigger {
pub fn new(git_state: Model<GitState>) -> Self {
Self {
git_state,
cursor_style: CursorStyle::PointingHand,
selected: false,
}
}
}
impl Clickable for RepoSelectorTrigger {
fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
// TODO:
self
}
fn cursor_style(mut self, cursor_style: CursorStyle) -> Self {
self.cursor_style = cursor_style;
self
}
}
impl Toggleable for RepoSelectorTrigger {
fn toggle_state(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
impl RenderOnce for RepoSelectorTrigger {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let active_repo = self.git_state.read(cx).active_repository();
let repo_name = active_repo
.and_then(|(_, repo, _)| {
// Extract the repository name from the work_directory path
repo.work_directory
.path
.file_name()
.and_then(|name| name.to_str())
.map(String::from)
})
.unwrap_or_else(|| "No repository".to_string());
Button::new("repo-selector", repo_name)
.icon(IconName::GitBranch)
.tooltip(|cx| Tooltip::text("Select repository", cx))
}
}

View File

@@ -306,7 +306,7 @@ impl RepositoryEntry {
/// project root and the .git folder is located in a parent directory.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct WorkDirectory {
path: Arc<Path>,
pub path: Arc<Path>,
/// If location_in_repo is set, it means the .git folder is external
/// and in a parent folder of the project root.