@@ -51,6 +51,7 @@ use panel::{
use project::{
Fs, Project, ProjectPath,
git_store::{GitStoreEvent, Repository, RepositoryEvent, RepositoryId, pending_op},
+ project_settings::{GitPathStyle, ProjectSettings},
};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore, StatusStyle};
@@ -3954,6 +3955,7 @@ impl GitPanel {
cx: &Context<Self>,
) -> AnyElement {
let path_style = self.project.read(cx).path_style(cx);
+ let git_path_style = ProjectSettings::get_global(cx).git.path_style;
let display_name = entry.display_name(path_style);
let selected = self.selected_entry == Some(ix);
@@ -4053,7 +4055,6 @@ impl GitPanel {
} else {
cx.theme().colors().ghost_element_active
};
-
h_flex()
.id(id)
.h(self.list_item_height())
@@ -4151,28 +4152,70 @@ impl GitPanel {
h_flex()
.items_center()
.flex_1()
- // .overflow_hidden()
- .when_some(entry.parent_dir(path_style), |this, parent| {
- if !parent.is_empty() {
- this.child(
- self.entry_label(
- format!("{parent}{}", path_style.separator()),
- path_color,
- )
- .when(status.is_deleted(), |this| this.strikethrough()),
- )
- } else {
- this
- }
- })
- .child(
- self.entry_label(display_name, label_color)
- .when(status.is_deleted(), |this| this.strikethrough()),
- ),
+ .child(h_flex().items_center().flex_1().map(|this| {
+ self.path_formatted(
+ this,
+ entry.parent_dir(path_style),
+ path_color,
+ display_name,
+ label_color,
+ path_style,
+ git_path_style,
+ status.is_deleted(),
+ )
+ })),
)
.into_any_element()
}
+ fn path_formatted(
+ &self,
+ parent: Div,
+ directory: Option<String>,
+ path_color: Color,
+ file_name: String,
+ label_color: Color,
+ path_style: PathStyle,
+ git_path_style: GitPathStyle,
+ strikethrough: bool,
+ ) -> Div {
+ parent
+ .when(git_path_style == GitPathStyle::FileNameFirst, |this| {
+ this.child(
+ self.entry_label(
+ match directory.as_ref().is_none_or(|d| d.is_empty()) {
+ true => file_name.clone(),
+ false => format!("{file_name} "),
+ },
+ label_color,
+ )
+ .when(strikethrough, Label::strikethrough),
+ )
+ })
+ .when_some(directory, |this, dir| {
+ match (
+ !dir.is_empty(),
+ git_path_style == GitPathStyle::FileNameFirst,
+ ) {
+ (true, true) => this.child(
+ self.entry_label(dir, path_color)
+ .when(strikethrough, Label::strikethrough),
+ ),
+ (true, false) => this.child(
+ self.entry_label(format!("{dir}{}", path_style.separator()), path_color)
+ .when(strikethrough, Label::strikethrough),
+ ),
+ _ => this,
+ }
+ })
+ .when(git_path_style == GitPathStyle::FilePathFirst, |this| {
+ this.child(
+ self.entry_label(file_name, label_color)
+ .when(strikethrough, Label::strikethrough),
+ )
+ })
+ }
+
fn has_write_access(&self, cx: &App) -> bool {
!self.project.read(cx).is_read_only(cx)
}
@@ -348,6 +348,26 @@ pub struct GitSettings {
///
/// Default: staged_hollow
pub hunk_style: settings::GitHunkStyleSetting,
+ /// How file paths are displayed in the git gutter.
+ ///
+ /// Default: file_name_first
+ pub path_style: GitPathStyle,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Default)]
+pub enum GitPathStyle {
+ #[default]
+ FileNameFirst,
+ FilePathFirst,
+}
+
+impl From<settings::GitPathStyle> for GitPathStyle {
+ fn from(style: settings::GitPathStyle) -> Self {
+ match style {
+ settings::GitPathStyle::FileNameFirst => GitPathStyle::FileNameFirst,
+ settings::GitPathStyle::FilePathFirst => GitPathStyle::FilePathFirst,
+ }
+ }
}
#[derive(Clone, Copy, Debug)]
@@ -501,6 +521,7 @@ impl Settings for ProjectSettings {
}
},
hunk_style: git.hunk_style.unwrap(),
+ path_style: git.path_style.unwrap().into(),
};
Self {
context_servers: project
@@ -311,6 +311,10 @@ pub struct GitSettings {
///
/// Default: staged_hollow
pub hunk_style: Option<GitHunkStyleSetting>,
+ /// How file paths are displayed in the git gutter.
+ ///
+ /// Default: file_name_first
+ pub path_style: Option<GitPathStyle>,
}
#[derive(
@@ -406,6 +410,28 @@ pub enum GitHunkStyleSetting {
UnstagedHollow,
}
+#[derive(
+ Copy,
+ Clone,
+ Debug,
+ PartialEq,
+ Default,
+ Serialize,
+ Deserialize,
+ JsonSchema,
+ MergeFrom,
+ strum::VariantArray,
+ strum::VariantNames,
+)]
+#[serde(rename_all = "snake_case")]
+pub enum GitPathStyle {
+ /// Show file name first, then path
+ #[default]
+ FileNameFirst,
+ /// Show full path first
+ FilePathFirst,
+}
+
#[skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct DiagnosticsSettingsContent {
@@ -5494,6 +5494,19 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
metadata: None,
files: USER,
}),
+ SettingsPageItem::SettingItem(SettingItem {
+ title: "Path Style",
+ description: "Should the name or path be displayed first in the git view.",
+ field: Box::new(SettingField {
+ json_path: Some("git.path_style"),
+ pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
+ write: |settings_content, value| {
+ settings_content.git.get_or_insert_default().path_style = value;
+ },
+ }),
+ metadata: None,
+ files: USER,
+ }),
],
},
SettingsPage {