feat: style git repo root (#2010)

This commit is contained in:
t-mangoe 2021-11-10 04:12:40 +09:00 committed by GitHub
parent 9df7c7d256
commit b07abc990e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 12 deletions

View File

@ -786,7 +786,8 @@ it would have been `nixpkgs/pkgs`.
| `read_only` | `"🔒"` | The symbol indicating current directory is read only. |
| `read_only_style` | `"red"` | The style for the read only symbol. |
| `truncation_symbol` | `""` | The symbol to prefix to truncated paths. eg: "…/" |
| `home_symbol` | `"~"` | The symbol indicating home directory. |
| `repo_root_style` | `None` | The style for the root of the git repo when `truncate_to_repo` option is set to false.|
| `home_symbol` | `"~"` | The symbol indicating home directory. |
<details>
<summary>This module has a few advanced configuration options that control how the directory is displayed.</summary>

View File

@ -12,7 +12,9 @@ pub struct DirectoryConfig<'a> {
pub fish_style_pwd_dir_length: i64,
pub use_logical_path: bool,
pub format: &'a str,
pub repo_root_format: &'a str,
pub style: &'a str,
pub repo_root_style: Option<&'a str>,
pub disabled: bool,
pub read_only: &'a str,
pub read_only_style: &'a str,
@ -29,7 +31,9 @@ impl<'a> Default for DirectoryConfig<'a> {
use_logical_path: true,
substitutions: IndexMap::new(),
format: "[$path]($style)[$read_only]($read_only_style) ",
repo_root_format: "[$before_root_path]($style)[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) ",
style: "cyan bold",
repo_root_style: None,
disabled: false,
read_only: "🔒",
read_only_style: "red",

View File

@ -50,17 +50,16 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
log::debug!("Display dir: {:?}", &display_dir);
// Attempt repository path contraction (if we are in a git repository)
let repo = if config.truncate_to_repo {
context.get_repo().ok()
// Otherwise use the logical path, automatically contracting
let repo = context.get_repo().ok();
let dir_string = if config.truncate_to_repo {
repo.and_then(|r| r.root.as_ref())
.filter(|&root| root != &home_dir)
.and_then(|root| contract_repo_path(display_dir, root))
} else {
None
};
let dir_string = repo
.and_then(|r| r.root.as_ref())
.filter(|root| *root != &home_dir)
.and_then(|root| contract_repo_path(display_dir, root));
// Otherwise use the logical path, automatically contracting
// the home directory if required.
let dir_string =
dir_string.unwrap_or_else(|| contract_path(display_dir, &home_dir, &home_symbol));
@ -92,18 +91,44 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
String::from("")
};
let displayed_path = prefix + &dir_string;
let lock_symbol = String::from(config.read_only);
let path_vec = match &repo.and_then(|r| r.root.as_ref()) {
Some(repo_root) if config.repo_root_style.is_some() => {
let contracted_path = contract_repo_path(display_dir, repo_root)?;
let repo_path_vec: Vec<&str> = contracted_path.split('/').collect();
let after_repo_root = contracted_path.replacen(repo_path_vec[0], "", 1);
let num_segments_after_root = after_repo_root.split('/').count();
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
if ((num_segments_after_root - 1) as i64) < config.truncation_length {
let root = repo_path_vec[0];
let before = dir_string.replace(&contracted_path, "");
[prefix + &before, root.to_string(), after_repo_root]
} else {
["".to_string(), "".to_string(), prefix + &dir_string]
}
}
_ => ["".to_string(), "".to_string(), prefix + &dir_string],
};
let lock_symbol = String::from(config.read_only);
let display_format = if path_vec[0].is_empty() && path_vec[1].is_empty() {
config.format
} else {
config.repo_root_format
};
let repo_root_style = config.repo_root_style.unwrap_or(config.style);
let parsed = StringFormatter::new(display_format).and_then(|formatter| {
formatter
.map_style(|variable| match variable {
"style" => Some(Ok(config.style)),
"read_only_style" => Some(Ok(config.read_only_style)),
"repo_root_style" => Some(Ok(repo_root_style)),
_ => None,
})
.map(|variable| match variable {
"path" => Some(Ok(&displayed_path)),
"path" => Some(Ok(&path_vec[2])),
"before_root_path" => Some(Ok(&path_vec[0])),
"repo_root" => Some(Ok(&path_vec[1])),
"read_only" => {
if is_readonly_dir(physical_dir) {
Some(Ok(&lock_symbol))
@ -1506,6 +1531,60 @@ mod tests {
assert_eq!(expected, actual);
}
#[test]
fn highlight_git_root_dir() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above").join("repo");
let dir = repo_dir.join("src/sub/path");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 5
truncate_to_repo = true
repo_root_style = "bold red"
})
.path(dir)
.collect();
let expected = Some(format!(
"{}{}repo{} ",
Color::Cyan.bold().prefix(),
Color::Red.prefix(),
Color::Cyan.paint("/src/sub/path")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
#[test]
fn highlight_git_root_dir_config_change() -> io::Result<()> {
let (tmp_dir, _) = make_known_tempdir(Path::new("/tmp"))?;
let repo_dir = tmp_dir.path().join("above").join("repo");
let dir = repo_dir.join("src/sub/path");
fs::create_dir_all(&dir)?;
init_repo(&repo_dir).unwrap();
let actual = ModuleRenderer::new("directory")
.config(toml::toml! {
[directory]
truncation_length = 5
truncation_symbol = "…/"
truncate_to_repo = false
repo_root_style = "green"
})
.path(dir)
.collect();
let expected = Some(format!(
"{}{}repo{} ",
Color::Cyan.bold().paint("…/above/"),
Color::Green.prefix(),
Color::Cyan.bold().paint("/src/sub/path")
));
assert_eq!(expected, actual);
tmp_dir.close()
}
// sample for invalid unicode from https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_string_lossy
#[cfg(any(unix, target_os = "redox"))]
fn invalid_path() -> PathBuf {