git: Move all state into the panel (#23185)
This should fix the problem with the panel not updating when switching projects. Release Notes: - N/A
This commit is contained in:
parent
d578f5ac37
commit
e265e69429
2 changed files with 81 additions and 121 deletions
|
@ -88,7 +88,7 @@ pub struct GitPanel {
|
|||
selected_entry: Option<usize>,
|
||||
show_scrollbar: bool,
|
||||
rebuild_requested: Arc<AtomicBool>,
|
||||
git_state: Model<GitState>,
|
||||
git_state: GitState,
|
||||
commit_editor: View<Editor>,
|
||||
/// The visible entries in the list, accounting for folding & expanded state.
|
||||
///
|
||||
|
@ -112,11 +112,8 @@ impl GitPanel {
|
|||
let fs = workspace.app_state().fs.clone();
|
||||
let project = workspace.project().clone();
|
||||
let language_registry = workspace.app_state().languages.clone();
|
||||
let git_state = GitState::get_global(cx);
|
||||
let current_commit_message = {
|
||||
let state = git_state.read(cx);
|
||||
state.commit_message.clone()
|
||||
};
|
||||
let mut git_state = GitState::new(cx);
|
||||
let current_commit_message = git_state.commit_message.clone();
|
||||
|
||||
let git_panel = cx.new_view(|cx: &mut ViewContext<Self>| {
|
||||
let focus_handle = cx.focus_handle();
|
||||
|
@ -128,89 +125,74 @@ impl GitPanel {
|
|||
cx.subscribe(&project, move |this, project, event, cx| {
|
||||
use project::Event;
|
||||
|
||||
let git_state = &mut this.git_state;
|
||||
let first_worktree_id = project.read(cx).worktrees(cx).next().map(|worktree| {
|
||||
let snapshot = worktree.read(cx).snapshot();
|
||||
snapshot.id()
|
||||
});
|
||||
let first_repo_in_project = first_repository_in_project(&project, cx);
|
||||
|
||||
// TODO: Don't get another git_state here
|
||||
// was running into a borrow issue
|
||||
let git_state = GitState::get_global(cx);
|
||||
|
||||
match event {
|
||||
project::Event::WorktreeRemoved(id) => {
|
||||
git_state.update(cx, |state, _| {
|
||||
state.all_repositories.remove(id);
|
||||
let Some((worktree_id, _, _)) = state.active_repository.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if worktree_id == id {
|
||||
state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
});
|
||||
git_state.all_repositories.remove(id);
|
||||
let Some((worktree_id, _, _)) = git_state.active_repository.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if worktree_id == id {
|
||||
git_state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
}
|
||||
project::Event::WorktreeOrderChanged => {
|
||||
// activate the new first worktree if the first was moved
|
||||
let Some(first_id) = first_worktree_id else {
|
||||
return;
|
||||
};
|
||||
git_state.update(cx, |state, _| {
|
||||
if !state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(id, _, _)| id == &first_id)
|
||||
{
|
||||
state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
});
|
||||
if !git_state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(id, _, _)| id == &first_id)
|
||||
{
|
||||
git_state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
}
|
||||
Event::WorktreeAdded(id) => {
|
||||
git_state.update(cx, |state, cx| {
|
||||
let Some(worktree) = project.read(cx).worktree_for_id(*id, cx) else {
|
||||
return;
|
||||
};
|
||||
let snapshot = worktree.read(cx).snapshot();
|
||||
state
|
||||
.all_repositories
|
||||
.insert(*id, snapshot.repositories().clone());
|
||||
});
|
||||
let Some(worktree) = project.read(cx).worktree_for_id(*id, cx) else {
|
||||
return;
|
||||
};
|
||||
let snapshot = worktree.read(cx).snapshot();
|
||||
git_state
|
||||
.all_repositories
|
||||
.insert(*id, snapshot.repositories().clone());
|
||||
let Some(first_id) = first_worktree_id else {
|
||||
return;
|
||||
};
|
||||
git_state.update(cx, |state, _| {
|
||||
if !state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(id, _, _)| id == &first_id)
|
||||
{
|
||||
state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
});
|
||||
if !git_state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(id, _, _)| id == &first_id)
|
||||
{
|
||||
git_state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
}
|
||||
project::Event::WorktreeUpdatedEntries(id, _) => {
|
||||
git_state.update(cx, |state, _| {
|
||||
if state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(active_id, _, _)| active_id == id)
|
||||
{
|
||||
state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
});
|
||||
if git_state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.is_some_and(|(active_id, _, _)| active_id == id)
|
||||
{
|
||||
git_state.active_repository = first_repo_in_project;
|
||||
this.schedule_update();
|
||||
}
|
||||
}
|
||||
project::Event::WorktreeUpdatedGitRepositories(_) => {
|
||||
let Some(first) = first_repo_in_project else {
|
||||
return;
|
||||
};
|
||||
git_state.update(cx, |state, _| {
|
||||
state.active_repository = Some(first);
|
||||
this.schedule_update();
|
||||
});
|
||||
git_state.active_repository = Some(first);
|
||||
this.schedule_update();
|
||||
}
|
||||
project::Event::Closed => {
|
||||
this.reveal_in_editor = Task::ready(());
|
||||
|
@ -272,20 +254,18 @@ impl GitPanel {
|
|||
|
||||
let scroll_handle = UniformListScrollHandle::new();
|
||||
|
||||
git_state.update(cx, |state, cx| {
|
||||
let mut visible_worktrees = project.read(cx).visible_worktrees(cx);
|
||||
let Some(first_worktree) = visible_worktrees.next() else {
|
||||
return;
|
||||
};
|
||||
drop(visible_worktrees);
|
||||
let mut visible_worktrees = project.read(cx).visible_worktrees(cx);
|
||||
let first_worktree = visible_worktrees.next();
|
||||
drop(visible_worktrees);
|
||||
if let Some(first_worktree) = first_worktree {
|
||||
let snapshot = first_worktree.read(cx).snapshot();
|
||||
|
||||
if let Some((repo, git_repo)) =
|
||||
first_worktree_repository(&project, snapshot.id(), cx)
|
||||
{
|
||||
state.activate_repository(snapshot.id(), repo, git_repo);
|
||||
git_state.activate_repository(snapshot.id(), repo, git_repo);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let rebuild_requested = Arc::new(AtomicBool::new(false));
|
||||
let flag = rebuild_requested.clone();
|
||||
|
@ -569,18 +549,16 @@ impl GitPanel {
|
|||
.and_then(|i| self.visible_entries.get(i))
|
||||
}
|
||||
|
||||
fn toggle_staged_for_entry(&self, entry: &GitListEntry, cx: &mut ViewContext<Self>) {
|
||||
self.git_state
|
||||
.clone()
|
||||
.update(cx, |state, _| match entry.status.is_staged() {
|
||||
Some(true) | None => state.unstage_entry(entry.repo_path.clone()),
|
||||
Some(false) => state.stage_entry(entry.repo_path.clone()),
|
||||
});
|
||||
fn toggle_staged_for_entry(&mut self, entry: &GitListEntry, cx: &mut ViewContext<Self>) {
|
||||
match entry.status.is_staged() {
|
||||
Some(true) | None => self.git_state.unstage_entry(entry.repo_path.clone()),
|
||||
Some(false) => self.git_state.stage_entry(entry.repo_path.clone()),
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn toggle_staged_for_selected(&mut self, _: &ToggleStaged, cx: &mut ViewContext<Self>) {
|
||||
if let Some(selected_entry) = self.get_selected_entry() {
|
||||
if let Some(selected_entry) = self.get_selected_entry().cloned() {
|
||||
self.toggle_staged_for_entry(&selected_entry, cx);
|
||||
}
|
||||
}
|
||||
|
@ -595,11 +573,14 @@ impl GitPanel {
|
|||
}
|
||||
|
||||
fn open_entry(&self, entry: &GitListEntry, cx: &mut ViewContext<Self>) {
|
||||
let Some((worktree_id, path)) = GitState::get_global(cx).update(cx, |state, _| {
|
||||
state.active_repository.as_ref().and_then(|(id, repo, _)| {
|
||||
Some((*id, repo.work_directory.unrelativize(&entry.repo_path)?))
|
||||
})
|
||||
}) else {
|
||||
let Some((worktree_id, path)) =
|
||||
self.git_state
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.and_then(|(id, repo, _)| {
|
||||
Some((*id, repo.work_directory.unrelativize(&entry.repo_path)?))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let path = (worktree_id, path).into();
|
||||
|
@ -612,7 +593,7 @@ impl GitPanel {
|
|||
cx.emit(Event::OpenedEntry { path });
|
||||
}
|
||||
|
||||
fn stage_all(&mut self, _: &StageAll, cx: &mut ViewContext<Self>) {
|
||||
fn stage_all(&mut self, _: &StageAll, _cx: &mut ViewContext<Self>) {
|
||||
let to_stage = self
|
||||
.visible_entries
|
||||
.iter_mut()
|
||||
|
@ -623,19 +604,16 @@ impl GitPanel {
|
|||
})
|
||||
.collect();
|
||||
self.all_staged = Some(true);
|
||||
self.git_state
|
||||
.update(cx, |state, _| state.stage_entries(to_stage));
|
||||
self.git_state.stage_entries(to_stage);
|
||||
}
|
||||
|
||||
fn unstage_all(&mut self, _: &UnstageAll, cx: &mut ViewContext<Self>) {
|
||||
fn unstage_all(&mut self, _: &UnstageAll, _cx: &mut ViewContext<Self>) {
|
||||
// This should only be called when all entries are staged.
|
||||
for entry in &mut self.visible_entries {
|
||||
entry.is_staged = Some(false);
|
||||
}
|
||||
self.all_staged = Some(false);
|
||||
self.git_state.update(cx, |state, _| {
|
||||
state.unstage_all();
|
||||
});
|
||||
self.git_state.unstage_all();
|
||||
}
|
||||
|
||||
fn discard_all(&mut self, _: &RevertAll, _cx: &mut ViewContext<Self>) {
|
||||
|
@ -644,8 +622,7 @@ impl GitPanel {
|
|||
}
|
||||
|
||||
fn clear_message(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.git_state
|
||||
.update(cx, |state, _cx| state.clear_commit_message());
|
||||
self.git_state.clear_commit_message();
|
||||
self.commit_editor
|
||||
.update(cx, |editor, cx| editor.set_text("", cx));
|
||||
}
|
||||
|
@ -713,11 +690,9 @@ impl GitPanel {
|
|||
|
||||
#[track_caller]
|
||||
fn update_visible_entries(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let git_state = self.git_state.read(cx);
|
||||
|
||||
self.visible_entries.clear();
|
||||
|
||||
let Some((_, repo, _)) = git_state.active_repository().as_ref() else {
|
||||
let Some((_, repo, _)) = self.git_state.active_repository().as_ref() else {
|
||||
// Just clear entries if no repository is active.
|
||||
cx.notify();
|
||||
return;
|
||||
|
@ -790,9 +765,7 @@ impl GitPanel {
|
|||
if let language::BufferEvent::Reparsed | language::BufferEvent::Edited = event {
|
||||
let commit_message = self.commit_editor.update(cx, |editor, cx| editor.text(cx));
|
||||
|
||||
self.git_state.update(cx, |state, _cx| {
|
||||
state.commit_message = Some(commit_message.into());
|
||||
});
|
||||
self.git_state.commit_message = Some(commit_message.into());
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -1096,7 +1069,6 @@ impl GitPanel {
|
|||
entry_details: GitListEntry,
|
||||
cx: &ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
let state = self.git_state.clone();
|
||||
let repo_path = entry_details.repo_path.clone();
|
||||
let selected = self.selected_entry == Some(ix);
|
||||
let status_style = GitPanelSettings::get_global(cx).status_style;
|
||||
|
@ -1122,7 +1094,7 @@ impl GitPanel {
|
|||
let entry_id = ElementId::Name(format!("entry_{}", entry_details.display_name).into());
|
||||
let checkbox_id =
|
||||
ElementId::Name(format!("checkbox_{}", entry_details.display_name).into());
|
||||
let view_mode = state.read(cx).list_view_mode.clone();
|
||||
let view_mode = self.git_state.list_view_mode;
|
||||
let handle = cx.view().downgrade();
|
||||
|
||||
let end_slot = h_flex()
|
||||
|
@ -1185,15 +1157,13 @@ impl GitPanel {
|
|||
ToggleState::Selected => Some(true),
|
||||
ToggleState::Unselected => Some(false),
|
||||
ToggleState::Indeterminate => None,
|
||||
}
|
||||
});
|
||||
state.update(cx, {
|
||||
};
|
||||
let repo_path = repo_path.clone();
|
||||
move |state, _| match toggle {
|
||||
match toggle {
|
||||
ToggleState::Selected | ToggleState::Indeterminate => {
|
||||
state.stage_entry(repo_path);
|
||||
this.git_state.stage_entry(repo_path);
|
||||
}
|
||||
ToggleState::Unselected => state.unstage_entry(repo_path),
|
||||
ToggleState::Unselected => this.git_state.unstage_entry(repo_path),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use futures::channel::mpsc;
|
|||
use futures::StreamExt as _;
|
||||
use git::repository::{GitFileStatus, GitRepository, RepoPath};
|
||||
use git_panel_settings::GitPanelSettings;
|
||||
use gpui::{actions, AppContext, Context, Global, Hsla, Model, ModelContext};
|
||||
use gpui::{actions, AppContext, Hsla, Model};
|
||||
use project::{Project, WorktreeId};
|
||||
use std::sync::Arc;
|
||||
use sum_tree::SumTree;
|
||||
|
@ -35,21 +35,15 @@ actions!(
|
|||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
GitPanelSettings::register(cx);
|
||||
let git_state = cx.new_model(GitState::new);
|
||||
cx.set_global(GlobalGitState(git_state));
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum GitViewMode {
|
||||
#[default]
|
||||
List,
|
||||
Tree,
|
||||
}
|
||||
|
||||
struct GlobalGitState(Model<GitState>);
|
||||
|
||||
impl Global for GlobalGitState {}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum StatusAction {
|
||||
Stage,
|
||||
|
@ -72,10 +66,10 @@ pub struct GitState {
|
|||
}
|
||||
|
||||
impl GitState {
|
||||
pub fn new(cx: &mut ModelContext<'_, Self>) -> Self {
|
||||
pub fn new(cx: &AppContext) -> Self {
|
||||
let (updater_tx, mut updater_rx) =
|
||||
mpsc::unbounded::<(Arc<dyn GitRepository>, Vec<RepoPath>, StatusAction)>();
|
||||
cx.spawn(|_, cx| async move {
|
||||
cx.spawn(|cx| async move {
|
||||
while let Some((git_repo, paths, action)) = updater_rx.next().await {
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
|
@ -98,10 +92,6 @@ impl GitState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_global(cx: &mut AppContext) -> Model<GitState> {
|
||||
cx.global::<GlobalGitState>().0.clone()
|
||||
}
|
||||
|
||||
pub fn activate_repository(
|
||||
&mut self,
|
||||
worktree_id: WorktreeId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue