diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4b352e2d8298f3c9ae2c0d38bd6b443d62a61996..0de2dc8423b39ab2b336adb3cb17f79cc4a8f6e7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -20090,6 +20090,11 @@ impl Editor { self.show_indent_guides } + pub fn disable_indent_guides(&mut self) -> Option { + self.show_indent_guides = Some(false); + self.show_indent_guides + } + pub fn toggle_line_numbers( &mut self, _: &ToggleLineNumbers, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 3319af92eb04015bd3bd01760235e3dba0047975..fb9dc31a94441c81bccedfea66e2881acaf7ed82 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3915,6 +3915,8 @@ impl EditorElement { ) -> impl IntoElement { let editor = self.editor.read(cx); let multi_buffer = editor.buffer.read(cx); + let is_read_only = self.editor.read(cx).read_only(cx); + let file_status = multi_buffer .all_diff_hunks_expanded() .then(|| editor.status_for_buffer_id(for_excerpt.buffer_id, cx)) @@ -3967,7 +3969,7 @@ impl EditorElement { .gap_1p5() .when(is_sticky, |el| el.shadow_md()) .border_1() - .map(|div| { + .map(|border| { let border_color = if is_selected && is_folded && focus_handle.contains_focused(window, cx) @@ -3976,7 +3978,7 @@ impl EditorElement { } else { colors.border }; - div.border_color(border_color) + border.border_color(border_color) }) .bg(colors.editor_subheader_background) .hover(|style| style.bg(colors.element_hover)) @@ -4056,13 +4058,15 @@ impl EditorElement { }) .take(1), ) - .child( - h_flex() - .size_3() - .justify_center() - .flex_shrink_0() - .children(indicator), - ) + .when(!is_read_only, |this| { + this.child( + h_flex() + .size_3() + .justify_center() + .flex_shrink_0() + .children(indicator), + ) + }) .child( h_flex() .cursor_pointer() diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 31ac8139a63be218f652204ebe29d43e526c5a02..8a4504c1873193e81658c19c6b1115a9212e7760 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -1,7 +1,7 @@ use anyhow::{Context as _, Result}; use buffer_diff::{BufferDiff, BufferDiffSnapshot}; use editor::display_map::{BlockPlacement, BlockProperties, BlockStyle}; -use editor::{Addon, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer}; +use editor::{Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer}; use git::repository::{CommitDetails, CommitDiff, RepoPath}; use git::{GitHostingProviderRegistry, GitRemote, parse_git_remote_url}; use gpui::{ @@ -11,9 +11,8 @@ use gpui::{ }; use language::{ Anchor, Buffer, Capability, DiskState, File, LanguageRegistry, LineEnding, ReplicaId, Rope, - TextBuffer, ToPoint, + TextBuffer, }; -use multi_buffer::ExcerptInfo; use multi_buffer::PathKey; use project::{Project, WorktreeId, git_store::Repository}; use std::{ @@ -22,11 +21,9 @@ use std::{ sync::Arc, }; use theme::ActiveTheme; -use ui::{ - Avatar, Button, ButtonCommon, Clickable, Color, Icon, IconName, IconSize, Label, - LabelCommon as _, LabelSize, SharedString, div, h_flex, v_flex, -}; +use ui::{Avatar, DiffStat, Tooltip, prelude::*}; use util::{ResultExt, paths::PathStyle, rel_path::RelPath, truncate_and_trailoff}; +use workspace::item::TabTooltipContent; use workspace::{ Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, @@ -151,11 +148,11 @@ impl CommitView { let editor = cx.new(|cx| { let mut editor = Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx); + editor.disable_inline_diagnostics(); + editor.disable_indent_guides(); editor.set_expand_all_diff_hunks(cx); - editor.register_addon(CommitViewAddon { - multibuffer: multibuffer.downgrade(), - }); + editor }); let commit_sha = Arc::::from(commit.sha.as_ref()); @@ -357,6 +354,41 @@ impl CommitView { .into_any() } + fn calculate_changed_lines(&self, cx: &App) -> (u32, u32) { + let snapshot = self.multibuffer.read(cx).snapshot(cx); + let mut total_additions = 0u32; + let mut total_deletions = 0u32; + + let mut seen_buffers = std::collections::HashSet::new(); + for (_, buffer, _) in snapshot.excerpts() { + let buffer_id = buffer.remote_id(); + if !seen_buffers.insert(buffer_id) { + continue; + } + + let Some(diff) = snapshot.diff_for_buffer_id(buffer_id) else { + continue; + }; + + let base_text = diff.base_text(); + + for hunk in diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer) { + let added_rows = hunk.range.end.row.saturating_sub(hunk.range.start.row); + total_additions += added_rows; + + let base_start = base_text + .offset_to_point(hunk.diff_base_byte_range.start) + .row; + let base_end = base_text.offset_to_point(hunk.diff_base_byte_range.end).row; + let deleted_rows = base_end.saturating_sub(base_start); + + total_deletions += deleted_rows; + } + } + + (total_additions, total_deletions) + } + fn render_header(&self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let commit = &self.commit; let author_name = commit.author_name.clone(); @@ -380,46 +412,72 @@ impl CommitView { ) }); - v_flex() - .p_4() - .pl_0() - .gap_4() + let (additions, deletions) = self.calculate_changed_lines(cx); + + let commit_diff_stat = if additions > 0 || deletions > 0 { + Some(DiffStat::new( + "commit-diff-stat", + additions as usize, + deletions as usize, + )) + } else { + None + }; + + h_flex() .border_b_1() - .border_color(cx.theme().colors().border) + .border_color(cx.theme().colors().border_variant) + .child( + h_flex() + .w(self.editor.read(cx).last_gutter_dimensions().full_width()) + .justify_center() + .child(self.render_commit_avatar(&commit.sha, rems_from_px(48.), window, cx)), + ) .child( h_flex() + .py_4() + .pl_1() + .pr_4() + .w_full() .items_start() - .child( - h_flex() - .w(self.editor.read(cx).last_gutter_dimensions().full_width()) - .justify_center() - .child(self.render_commit_avatar( - &commit.sha, - gpui::rems(3.0), - window, - cx, - )), - ) + .justify_between() + .flex_wrap() .child( v_flex() - .gap_1() .child( h_flex() - .gap_3() - .items_baseline() + .gap_1() .child(Label::new(author_name).color(Color::Default)) .child( - Label::new(format!("commit {}", commit.sha)) - .color(Color::Muted), + Label::new(format!("Commit:{}", commit.sha)) + .color(Color::Muted) + .size(LabelSize::Small) + .truncate() + .buffer_font(cx), ), ) - .child(Label::new(date_string).color(Color::Muted)), + .child( + h_flex() + .gap_1p5() + .child( + Label::new(date_string) + .color(Color::Muted) + .size(LabelSize::Small), + ) + .child( + Label::new("•") + .color(Color::Ignored) + .size(LabelSize::Small), + ) + .children(commit_diff_stat), + ), ) - .child(div().flex_grow()) .children(github_url.map(|url| { Button::new("view_on_github", "View on GitHub") .icon(IconName::Github) - .style(ui::ButtonStyle::Subtle) + .icon_color(Color::Muted) + .icon_size(IconSize::Small) + .icon_position(IconPosition::Start) .on_click(move |_, _, cx| cx.open_url(&url)) })), ) @@ -714,55 +772,6 @@ impl language::File for GitBlob { // } // } -struct CommitViewAddon { - multibuffer: WeakEntity, -} - -impl Addon for CommitViewAddon { - fn render_buffer_header_controls( - &self, - excerpt: &ExcerptInfo, - _window: &Window, - cx: &App, - ) -> Option { - let multibuffer = self.multibuffer.upgrade()?; - let snapshot = multibuffer.read(cx).snapshot(cx); - let excerpts = snapshot.excerpts().collect::>(); - let current_idx = excerpts.iter().position(|(id, _, _)| *id == excerpt.id)?; - let (_, _, current_range) = &excerpts[current_idx]; - - let start_row = current_range.context.start.to_point(&excerpt.buffer).row; - - let prev_end_row = if current_idx > 0 { - let (_, prev_buffer, prev_range) = &excerpts[current_idx - 1]; - if prev_buffer.remote_id() == excerpt.buffer_id { - prev_range.context.end.to_point(&excerpt.buffer).row - } else { - 0 - } - } else { - 0 - }; - - let skipped_lines = start_row.saturating_sub(prev_end_row); - - if skipped_lines > 0 { - Some( - Label::new(format!("{} unchanged lines", skipped_lines)) - .color(Color::Muted) - .size(LabelSize::Small) - .into_any_element(), - ) - } else { - None - } - } - - fn to_any(&self) -> &dyn Any { - self - } -} - async fn build_buffer( mut text: String, blob: Arc, @@ -865,13 +874,28 @@ impl Item for CommitView { fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString { let short_sha = self.commit.sha.get(0..7).unwrap_or(&*self.commit.sha); let subject = truncate_and_trailoff(self.commit.message.split('\n').next().unwrap(), 20); - format!("{short_sha} - {subject}").into() + format!("{short_sha} — {subject}").into() } - fn tab_tooltip_text(&self, _: &App) -> Option { + fn tab_tooltip_content(&self, _: &App) -> Option { let short_sha = self.commit.sha.get(0..16).unwrap_or(&*self.commit.sha); let subject = self.commit.message.split('\n').next().unwrap(); - Some(format!("{short_sha} - {subject}").into()) + + Some(TabTooltipContent::Custom(Box::new(Tooltip::element({ + let subject = subject.to_string(); + let short_sha = short_sha.to_string(); + + move |_, _| { + v_flex() + .child(Label::new(subject.clone())) + .child( + Label::new(short_sha.clone()) + .color(Color::Muted) + .size(LabelSize::Small), + ) + .into_any_element() + } + })))) } fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) { @@ -988,12 +1012,11 @@ impl Item for CommitView { impl Render for CommitView { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let is_stash = self.stash.is_some(); - div() + + v_flex() .key_context(if is_stash { "StashDiff" } else { "CommitDiff" }) - .bg(cx.theme().colors().editor_background) - .flex() - .flex_col() .size_full() + .bg(cx.theme().colors().editor_background) .child(self.render_header(window, cx)) .child(div().flex_grow().child(self.editor.clone())) } @@ -1013,7 +1036,7 @@ impl EventEmitter for CommitViewToolbar {} impl Render for CommitViewToolbar { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() + div().hidden() } }