diff --git a/docs/config/README.md b/docs/config/README.md index 2ef2e40b..2ba70b45 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -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. |
This module has a few advanced configuration options that control how the directory is displayed. diff --git a/src/configs/directory.rs b/src/configs/directory.rs index 9f1b27ad..566ae439 100644 --- a/src/configs/directory.rs +++ b/src/configs/directory.rs @@ -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", diff --git a/src/modules/directory.rs b/src/modules/directory.rs index 6728eb7c..3f481070 100644 --- a/src/modules/directory.rs +++ b/src/modules/directory.rs @@ -50,17 +50,16 @@ pub fn module<'a>(context: &'a Context) -> Option> { 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> { 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 {