use crate::{git_panel::GitStatusEntry, git_status_icon};use anyhow::{Context as _, Result};use buffer_diff::DiffHunkSecondaryStatus;use editor::{ Direction, Editor, EditorEvent, EditorSettings, SplittableEditor, ToggleSplitDiff, actions::{GoToHunk, GoToPreviousHunk},};use fs::Fs;use git::{ Commit, Restore, StageAndNext, StageFile, ToggleStaged, UnstageAndNext, UnstageFile, repository::RepoPath, status::StageStatus,};use gpui::{ Action, AnyElement, App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, Task, WeakEntity, Window,};use language::{Buffer, HighlightedText};use multi_buffer::MultiBuffer;use project::{ Project, git_store::{Repository, RepositoryId},};use settings::{DiffViewStyle, Settings, SettingsStore, update_settings_file};use std::{ any::{Any, TypeId}, sync::Arc,};use ui::{ Color, DiffStat, Divider, Icon, IconButton, IconButtonShape, IconName, Label, LabelCommon as _, SharedString, Tooltip, prelude::*, vertical_divider,};use util::paths::{PathExt as _, PathStyle};use workspace::{ Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{ItemEvent, SaveOptions, TabContentParams}, notifications::NotifyTaskExt, searchable::SearchableItemHandle,}; pub struct SoloDiffView { repository: Entity<Repository>, repository_id: RepositoryId, repo_path: RepoPath, buffer: Entity<Buffer>, editor: Entity<SplittableEditor>, workspace: WeakEntity<Workspace>, _settings_subscription: Subscription,} impl SoloDiffView { pub fn open_or_focus( entry: GitStatusEntry, repository: Entity<Repository>, workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut App, ) -> Task<Result<Entity<Self>>> { let Some(workspace_entity) = workspace.upgrade() else { return Task::ready(Err(anyhow::anyhow!("workspace was dropped"))); }; let existing = workspace_entity .read(cx) .items_of_type::<SoloDiffView>(cx) .find(|item| item.read(cx).matches(&repository, &entry.repo_path, cx)); if let Some(existing) = existing { workspace_entity.update(cx, |workspace, cx| { workspace.activate_item(&existing, true, true, window, cx); }); existing.focus_handle(cx).focus(window, cx); return Task::ready(Ok(existing)); } let Some(project_path) = repository .read(cx) .repo_path_to_project_path(&entry.repo_path, cx) else { return Task::ready(Err(anyhow::anyhow!( "could not resolve repository path {:?}", entry.repo_path ))); }; let project = workspace_entity.read(cx).project().clone(); let repo_path = entry.repo_path; window.spawn(cx, async move |cx| { let buffer = project .update(cx, |project, cx| { project.open_buffer(project_path.clone(), cx) }) .await?; let diff = project .update(cx, |project, cx| { project.open_uncommitted_diff(buffer.clone(), cx) }) .await?; workspace_entity.update_in(cx, |workspace, window, cx| { let workspace_handle = cx.entity(); let view = cx.new(|cx| { Self::new( project, repository, repo_path, buffer, diff, workspace_handle, window, cx, ) }); workspace.add_item_to_active_pane(Box::new(view.clone()), None, true, window, cx); view }) }) } fn new( project: Entity<Project>, repository: Entity<Repository>, repo_path: RepoPath, buffer: Entity<Buffer>, diff: Entity<buffer_diff::BufferDiff>, workspace: Entity<Workspace>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let repository_id = repository.read(cx).id; let multibuffer = cx.new(|cx| { let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx); multibuffer.add_diff(diff, cx); multibuffer.set_all_diff_hunks_expanded(cx); multibuffer }); let editor = cx.new(|cx| { let editor = SplittableEditor::new( EditorSettings::get_global(cx).diff_view_style, multibuffer, project.clone(), workspace.clone(), window, cx, ); editor.rhs_editor().update(cx, |editor, cx| { editor.set_should_serialize(false, cx); let snapshot = editor.snapshot(window, cx); editor.go_to_hunk_before_or_after_position( &snapshot, language::Point::new(0, 0), Direction::Next, true, window, cx, ); }); editor }); let mut previous_diff_view_style = EditorSettings::get_global(cx).diff_view_style; let settings_subscription = cx.observe_global_in::<SettingsStore>(window, move |this, window, cx| { let diff_view_style = EditorSettings::get_global(cx).diff_view_style; if diff_view_style != previous_diff_view_style { this.editor.update(cx, |editor, cx| { if editor.diff_view_style() != diff_view_style { editor.toggle_split(&ToggleSplitDiff, window, cx); } }); previous_diff_view_style = diff_view_style; cx.notify(); } }); Self { repository, repository_id, repo_path, buffer, editor, workspace: workspace.downgrade(), _settings_subscription: settings_subscription, } } fn matches(&self, repository: &Entity<Repository>, repo_path: &RepoPath, cx: &App) -> bool { self.repository_id == repository.read(cx).id && &self.repo_path == repo_path } fn button_states(&self, cx: &App) -> SoloDiffButtonStates { let editor = self.editor.read(cx).rhs_editor().read(cx); let multibuffer = editor.buffer().read(cx); let snapshot = multibuffer.snapshot(cx); let prev_next = snapshot.diff_hunks().nth(1).is_some(); let mut selection = true; let mut ranges = editor .selections .disjoint_anchor_ranges() .collect::<Vec<_>>(); if !ranges.iter().any(|range| range.start != range.end) { selection = false; let anchor = editor.selections.newest_anchor().head(); if let Some((_, excerpt_range)) = snapshot.excerpt_containing(anchor..anchor) && let Some(range) = snapshot .anchor_in_buffer(excerpt_range.context.start) .zip(snapshot.anchor_in_buffer(excerpt_range.context.end)) .map(|(start, end)| start..end) { ranges = vec![range]; } else { ranges = Vec::new(); } } let mut stage = false; let mut unstage = false; for hunk in editor.diff_hunks_in_ranges(&ranges, &snapshot) { match hunk.status.secondary { DiffHunkSecondaryStatus::HasSecondaryHunk | DiffHunkSecondaryStatus::SecondaryHunkAdditionPending => { stage = true; } DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk => { stage = true; unstage = true; } DiffHunkSecondaryStatus::NoSecondaryHunk | DiffHunkSecondaryStatus::SecondaryHunkRemovalPending => { unstage = true; } } } let stage_status = self .repository .read(cx) .status_for_path(&self.repo_path) .map(|entry| entry.status.staging()) .unwrap_or(StageStatus::Unstaged); SoloDiffButtonStates { stage, unstage, restore: stage || unstage, prev_next, selection, stage_file: stage_status.has_unstaged(), unstage_file: stage_status.has_staged(), } } fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) { self.focus_handle(cx).focus(window, cx); let action = action.boxed_clone(); cx.defer(move |cx| { cx.dispatch_action(action.as_ref()); }); } fn change_file_stage(&self, stage: bool, window: &mut Window, cx: &mut Context<Self>) { let repository = self.repository.clone(); let repo_path = self.repo_path.clone(); let workspace = self.workspace.clone(); let task = cx.spawn(async move |_, cx| { repository .update(cx, |repository, cx| { if stage { repository.stage_entries(vec![repo_path], cx) } else { repository.unstage_entries(vec![repo_path], cx) } }) .await .with_context(|| { if stage { "failed to stage file" } else { "failed to unstage file" } }) }); task.detach_and_notify_err(workspace, window, cx); }} impl EventEmitter<EditorEvent> for SoloDiffView {} impl Focusable for SoloDiffView { fn focus_handle(&self, cx: &App) -> FocusHandle { self.editor.focus_handle(cx) }} impl Item for SoloDiffView { type Event = EditorEvent; fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> { Some(Icon::new(IconName::Diff).color(Color::Muted)) } fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement { Label::new(self.tab_content_text(params.detail.unwrap_or_default(), cx)) .color(if params.selected { Color::Default } else { Color::Muted }) .into_any_element() } fn tab_content_text(&self, _detail: usize, cx: &App) -> SharedString { self.buffer .read(cx) .file() .and_then(|file| { Some( file.full_path(cx) .file_name()? .to_string_lossy() .to_string(), ) }) .unwrap_or_else(|| { self.repo_path .as_ref() .display(PathStyle::local()) .into_owned() }) .into() } fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> { Some( self.buffer .read(cx) .file() .map(|file| file.full_path(cx).compact().to_string_lossy().into_owned()) .unwrap_or_else(|| { self.repo_path .as_ref() .display(PathStyle::local()) .into_owned() }) .into(), ) } fn to_item_events(event: &EditorEvent, f: &mut dyn FnMut(ItemEvent)) { Editor::to_item_events(event, f) } fn telemetry_event_text(&self) -> Option<&'static str> { Some("Solo Diff View Opened") } fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.editor.deactivated(window, cx); } fn act_as_type<'a>( &'a self, type_id: TypeId, self_handle: &'a Entity<Self>, cx: &'a App, ) -> Option<gpui::AnyEntity> { if type_id == TypeId::of::<Self>() { Some(self_handle.clone().into()) } else { self.editor.act_as_type(type_id, cx) } } fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> { Some(Box::new(self.editor.clone())) } fn for_each_project_item( &self, cx: &App, f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem), ) { self.editor.for_each_project_item(cx, f) } fn set_nav_history( &mut self, nav_history: ItemNavHistory, _: &mut Window, cx: &mut Context<Self>, ) { self.editor.update(cx, |editor, cx| { editor.rhs_editor().update(cx, |editor, _| { editor.set_nav_history(Some(nav_history)); }) }); } fn navigate( &mut self, data: Arc<dyn Any + Send>, window: &mut Window, cx: &mut Context<Self>, ) -> bool { self.editor.update(cx, |editor, cx| { editor .rhs_editor() .update(cx, |editor, cx| editor.navigate(data, window, cx)) }) } fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation { ToolbarItemLocation::PrimaryLeft } fn breadcrumbs(&self, cx: &App) -> Option<(Vec<HighlightedText>, Option<gpui::Font>)> { self.editor.breadcrumbs(cx) } fn added_to_workspace( &mut self, workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Self>, ) { self.editor.update(cx, |editor, cx| { editor.rhs_editor().update(cx, |editor, cx| { editor.added_to_workspace(workspace, window, cx) }) }); } fn can_save(&self, cx: &App) -> bool { self.editor.read(cx).rhs_editor().read(cx).can_save(cx) } fn save( &mut self, options: SaveOptions, project: Entity<Project>, window: &mut Window, cx: &mut Context<Self>, ) -> Task<Result<()>> { self.editor.save(options, project, window, cx) }} impl Render for SoloDiffView { fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement { self.editor.clone() }} pub struct SoloDiffStyleToolbar { solo_diff: Option<WeakEntity<SoloDiffView>>,} pub struct SoloDiffGitToolbar { solo_diff: Option<WeakEntity<SoloDiffView>>,} impl SoloDiffStyleToolbar { pub fn new(_: &mut Context<Self>) -> Self { Self { solo_diff: None } } fn solo_diff(&self) -> Option<Entity<SoloDiffView>> { self.solo_diff.as_ref()?.upgrade() } fn set_diff_view_style( &mut self, diff_view_style: DiffViewStyle, window: &mut Window, cx: &mut Context<Self>, ) { let Some(solo_diff) = self.solo_diff() else { return; }; let workspace = solo_diff.read(cx).workspace.clone(); update_settings_file(<dyn Fs>::global(cx), cx, move |settings, _| { settings.editor.diff_view_style = Some(diff_view_style); }); if let Some(workspace) = workspace.upgrade() { let splittable_editors = { workspace .read(cx) .items(cx) .filter_map(|item| item.act_as_type(TypeId::of::<SplittableEditor>(), cx)) .filter_map(|item| item.downcast::<SplittableEditor>().ok()) .collect::<Vec<_>>() }; for editor in splittable_editors { editor.update(cx, |editor, cx| { if editor.diff_view_style() != diff_view_style { editor.toggle_split(&ToggleSplitDiff, window, cx);Lines 1–500 of 787. Load more
use crate::{git_panel::GitStatusEntry, git_status_icon}; use anyhow::{Context as _, Result}; use buffer_diff::DiffHunkSecondaryStatus; use editor::{ Direction, Editor, EditorEvent, EditorSettings, SplittableEditor, ToggleSplitDiff, actions::{GoToHunk, GoToPreviousHunk}, }; use fs::Fs; use git::{ Commit, Restore, StageAndNext, StageFile, ToggleStaged, UnstageAndNext, UnstageFile, repository::RepoPath, status::StageStatus, }; use gpui::{ Action, AnyElement, App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, Task, WeakEntity, Window, }; use language::{Buffer, HighlightedText}; use multi_buffer::MultiBuffer; use project::{ Project, git_store::{Repository, RepositoryId}, }; use settings::{DiffViewStyle, Settings, SettingsStore, update_settings_file}; use std::{ any::{Any, TypeId}, sync::Arc, }; use ui::{ Color, DiffStat, Divider, Icon, IconButton, IconButtonShape, IconName, Label, LabelCommon as _, SharedString, Tooltip, prelude::*, vertical_divider, }; use util::paths::{PathExt as _, PathStyle}; use workspace::{ Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{ItemEvent, SaveOptions, TabContentParams}, notifications::NotifyTaskExt, searchable::SearchableItemHandle, }; pub struct SoloDiffView { repository: Entity<Repository>, repository_id: RepositoryId, repo_path: RepoPath, buffer: Entity<Buffer>, editor: Entity<SplittableEditor>, workspace: WeakEntity<Workspace>, _settings_subscription: Subscription, } impl SoloDiffView { pub fn open_or_focus( entry: GitStatusEntry, repository: Entity<Repository>, workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut App, ) -> Task<Result<Entity<Self>>> { let Some(workspace_entity) = workspace.upgrade() else { return Task::ready(Err(anyhow::anyhow!("workspace was dropped"))); }; let existing = workspace_entity .read(cx) .items_of_type::<SoloDiffView>(cx) .find(|item| item.read(cx).matches(&repository, &entry.repo_path, cx)); if let Some(existing) = existing { workspace_entity.update(cx, |workspace, cx| { workspace.activate_item(&existing, true, true, window, cx); }); existing.focus_handle(cx).focus(window, cx); return Task::ready(Ok(existing)); } let Some(project_path) = repository .read(cx) .repo_path_to_project_path(&entry.repo_path, cx) else { return Task::ready(Err(anyhow::anyhow!( "could not resolve repository path {:?}", entry.repo_path ))); }; let project = workspace_entity.read(cx).project().clone(); let repo_path = entry.repo_path; window.spawn(cx, async move |cx| { let buffer = project .update(cx, |project, cx| { project.open_buffer(project_path.clone(), cx) }) .await?; let diff = project .update(cx, |project, cx| { project.open_uncommitted_diff(buffer.clone(), cx) }) .await?; workspace_entity.update_in(cx, |workspace, window, cx| { let workspace_handle = cx.entity(); let view = cx.new(|cx| { Self::new( project, repository, repo_path, buffer, diff, workspace_handle, window, cx, ) }); workspace.add_item_to_active_pane(Box::new(view.clone()), None, true, window, cx); view }) }) } fn new( project: Entity<Project>, repository: Entity<Repository>, repo_path: RepoPath, buffer: Entity<Buffer>, diff: Entity<buffer_diff::BufferDiff>, workspace: Entity<Workspace>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let repository_id = repository.read(cx).id; let multibuffer = cx.new(|cx| { let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx); multibuffer.add_diff(diff, cx); multibuffer.set_all_diff_hunks_expanded(cx); multibuffer }); let editor = cx.new(|cx| { let editor = SplittableEditor::new( EditorSettings::get_global(cx).diff_view_style, multibuffer, project.clone(), workspace.clone(), window, cx, ); editor.rhs_editor().update(cx, |editor, cx| { editor.set_should_serialize(false, cx); let snapshot = editor.snapshot(window, cx); editor.go_to_hunk_before_or_after_position( &snapshot, language::Point::new(0, 0), Direction::Next, true, window, cx, ); }); editor }); let mut previous_diff_view_style = EditorSettings::get_global(cx).diff_view_style; let settings_subscription = cx.observe_global_in::<SettingsStore>(window, move |this, window, cx| { let diff_view_style = EditorSettings::get_global(cx).diff_view_style; if diff_view_style != previous_diff_view_style { this.editor.update(cx, |editor, cx| { if editor.diff_view_style() != diff_view_style { editor.toggle_split(&ToggleSplitDiff, window, cx); } }); previous_diff_view_style = diff_view_style; cx.notify(); } }); Self { repository, repository_id, repo_path, buffer, editor, workspace: workspace.downgrade(), _settings_subscription: settings_subscription, } } fn matches(&self, repository: &Entity<Repository>, repo_path: &RepoPath, cx: &App) -> bool { self.repository_id == repository.read(cx).id && &self.repo_path == repo_path } fn button_states(&self, cx: &App) -> SoloDiffButtonStates { let editor = self.editor.read(cx).rhs_editor().read(cx); let multibuffer = editor.buffer().read(cx); let snapshot = multibuffer.snapshot(cx); let prev_next = snapshot.diff_hunks().nth(1).is_some(); let mut selection = true; let mut ranges = editor .selections .disjoint_anchor_ranges() .collect::<Vec<_>>(); if !ranges.iter().any(|range| range.start != range.end) { selection = false; let anchor = editor.selections.newest_anchor().head(); if let Some((_, excerpt_range)) = snapshot.excerpt_containing(anchor..anchor) && let Some(range) = snapshot .anchor_in_buffer(excerpt_range.context.start) .zip(snapshot.anchor_in_buffer(excerpt_range.context.end)) .map(|(start, end)| start..end) { ranges = vec![range]; } else { ranges = Vec::new(); } } let mut stage = false; let mut unstage = false; for hunk in editor.diff_hunks_in_ranges(&ranges, &snapshot) { match hunk.status.secondary { DiffHunkSecondaryStatus::HasSecondaryHunk | DiffHunkSecondaryStatus::SecondaryHunkAdditionPending => { stage = true; } DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk => { stage = true; unstage = true; } DiffHunkSecondaryStatus::NoSecondaryHunk | DiffHunkSecondaryStatus::SecondaryHunkRemovalPending => { unstage = true; } } } let stage_status = self .repository .read(cx) .status_for_path(&self.repo_path) .map(|entry| entry.status.staging()) .unwrap_or(StageStatus::Unstaged); SoloDiffButtonStates { stage, unstage, restore: stage || unstage, prev_next, selection, stage_file: stage_status.has_unstaged(), unstage_file: stage_status.has_staged(), } } fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) { self.focus_handle(cx).focus(window, cx); let action = action.boxed_clone(); cx.defer(move |cx| { cx.dispatch_action(action.as_ref()); }); } fn change_file_stage(&self, stage: bool, window: &mut Window, cx: &mut Context<Self>) { let repository = self.repository.clone(); let repo_path = self.repo_path.clone(); let workspace = self.workspace.clone(); let task = cx.spawn(async move |_, cx| { repository .update(cx, |repository, cx| { if stage { repository.stage_entries(vec![repo_path], cx) } else { repository.unstage_entries(vec![repo_path], cx) } }) .await .with_context(|| { if stage { "failed to stage file" } else { "failed to unstage file" } }) }); task.detach_and_notify_err(workspace, window, cx); } } impl EventEmitter<EditorEvent> for SoloDiffView {} impl Focusable for SoloDiffView { fn focus_handle(&self, cx: &App) -> FocusHandle { self.editor.focus_handle(cx) } } impl Item for SoloDiffView { type Event = EditorEvent; fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> { Some(Icon::new(IconName::Diff).color(Color::Muted)) } fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement { Label::new(self.tab_content_text(params.detail.unwrap_or_default(), cx)) .color(if params.selected { Color::Default } else { Color::Muted }) .into_any_element() } fn tab_content_text(&self, _detail: usize, cx: &App) -> SharedString { self.buffer .read(cx) .file() .and_then(|file| { Some( file.full_path(cx) .file_name()? .to_string_lossy() .to_string(), ) }) .unwrap_or_else(|| { self.repo_path .as_ref() .display(PathStyle::local()) .into_owned() }) .into() } fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString> { Some( self.buffer .read(cx) .file() .map(|file| file.full_path(cx).compact().to_string_lossy().into_owned()) .unwrap_or_else(|| { self.repo_path .as_ref() .display(PathStyle::local()) .into_owned() }) .into(), ) } fn to_item_events(event: &EditorEvent, f: &mut dyn FnMut(ItemEvent)) { Editor::to_item_events(event, f) } fn telemetry_event_text(&self) -> Option<&'static str> { Some("Solo Diff View Opened") } fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.editor.deactivated(window, cx); } fn act_as_type<'a>( &'a self, type_id: TypeId, self_handle: &'a Entity<Self>, cx: &'a App, ) -> Option<gpui::AnyEntity> { if type_id == TypeId::of::<Self>() { Some(self_handle.clone().into()) } else { self.editor.act_as_type(type_id, cx) } } fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> { Some(Box::new(self.editor.clone())) } fn for_each_project_item( &self, cx: &App, f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem), ) { self.editor.for_each_project_item(cx, f) } fn set_nav_history( &mut self, nav_history: ItemNavHistory, _: &mut Window, cx: &mut Context<Self>, ) { self.editor.update(cx, |editor, cx| { editor.rhs_editor().update(cx, |editor, _| { editor.set_nav_history(Some(nav_history)); }) }); } fn navigate( &mut self, data: Arc<dyn Any + Send>, window: &mut Window, cx: &mut Context<Self>, ) -> bool { self.editor.update(cx, |editor, cx| { editor .rhs_editor() .update(cx, |editor, cx| editor.navigate(data, window, cx)) }) } fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation { ToolbarItemLocation::PrimaryLeft } fn breadcrumbs(&self, cx: &App) -> Option<(Vec<HighlightedText>, Option<gpui::Font>)> { self.editor.breadcrumbs(cx) } fn added_to_workspace( &mut self, workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Self>, ) { self.editor.update(cx, |editor, cx| { editor.rhs_editor().update(cx, |editor, cx| { editor.added_to_workspace(workspace, window, cx) }) }); } fn can_save(&self, cx: &App) -> bool { self.editor.read(cx).rhs_editor().read(cx).can_save(cx) } fn save( &mut self, options: SaveOptions, project: Entity<Project>, window: &mut Window, cx: &mut Context<Self>, ) -> Task<Result<()>> { self.editor.save(options, project, window, cx) } } impl Render for SoloDiffView { fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement { self.editor.clone() } } pub struct SoloDiffStyleToolbar { solo_diff: Option<WeakEntity<SoloDiffView>>, } pub struct SoloDiffGitToolbar { solo_diff: Option<WeakEntity<SoloDiffView>>, } impl SoloDiffStyleToolbar { pub fn new(_: &mut Context<Self>) -> Self { Self { solo_diff: None } } fn solo_diff(&self) -> Option<Entity<SoloDiffView>> { self.solo_diff.as_ref()?.upgrade() } fn set_diff_view_style( &mut self, diff_view_style: DiffViewStyle, window: &mut Window, cx: &mut Context<Self>, ) { let Some(solo_diff) = self.solo_diff() else { return; }; let workspace = solo_diff.read(cx).workspace.clone(); update_settings_file(<dyn Fs>::global(cx), cx, move |settings, _| { settings.editor.diff_view_style = Some(diff_view_style); }); if let Some(workspace) = workspace.upgrade() { let splittable_editors = { workspace .read(cx) .items(cx) .filter_map(|item| item.act_as_type(TypeId::of::<SplittableEditor>(), cx)) .filter_map(|item| item.downcast::<SplittableEditor>().ok()) .collect::<Vec<_>>() }; for editor in splittable_editors { editor.update(cx, |editor, cx| { if editor.diff_view_style() != diff_view_style { editor.toggle_split(&ToggleSplitDiff, window, cx);