feat(directory): Add directory substitutions (#1183)

Adds an option to provide a table of strings to substitute in the
directory string. Fixes #1065.

Co-authored-by: Radu Butoi <butoi@google.com>
This commit is contained in:
Radu Butoi 2020-05-31 13:32:35 -04:00 committed by GitHub
parent 329b3c791d
commit ab1c3d1c54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 4 deletions

View File

@ -399,9 +399,19 @@ it would have been `nixpkgs/pkgs`.
| Variable | Default | Description |
| --------------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `substitutions` | | A table of substitutions to be made to the path. |
| `fish_style_pwd_dir_length` | `0` | The number of characters to use when applying fish shell pwd path logic. |
| `use_logical_path` | `true` | Displays the logical path provided by the shell (`PWD`) instead of the path from the OS. |
`substitutions` allows you to define arbitrary replacements for literal strings that occur in the path, for example long network
prefixes or development directories (i.e. Java). Note that this will disable the fish style PWD.
```toml
[directory.substitutions]
"/Volumes/network/path" = "/net"
"src/com/long/java/path" = "mypath"
```
`fish_style_pwd_dir_length` interacts with the standard truncation options in a way that can be surprising at first: if it's non-zero,
the components of the path that would normally be truncated are instead displayed with that many characters. For example, the path
`/built/this/city/on/rock/and/roll`, which would normally be displayed as as `rock/and/roll`, would be displayed as

View File

@ -1,4 +1,5 @@
use crate::config::{ModuleConfig, RootModuleConfig};
use std::collections::HashMap;
use ansi_term::{Color, Style};
use starship_module_config_derive::ModuleConfig;
@ -7,6 +8,7 @@ use starship_module_config_derive::ModuleConfig;
pub struct DirectoryConfig<'a> {
pub truncation_length: i64,
pub truncate_to_repo: bool,
pub substitutions: HashMap<String, &'a str>,
pub fish_style_pwd_dir_length: i64,
pub use_logical_path: bool,
pub prefix: &'a str,
@ -20,6 +22,7 @@ impl<'a> RootModuleConfig<'a> for DirectoryConfig<'a> {
truncation_length: 3,
truncate_to_repo: true,
fish_style_pwd_dir_length: 0,
substitutions: HashMap::new(),
use_logical_path: true,
prefix: "in ",
style: Color::Cyan.bold(),

View File

@ -1,4 +1,5 @@
use path_slash::PathExt;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use unicode_segmentation::UnicodeSegmentation;
@ -10,12 +11,15 @@ use crate::configs::directory::DirectoryConfig;
/// Creates a module with the current directory
///
/// Will perform path contraction and truncation.
/// Will perform path contraction, substitution, and truncation.
/// **Contraction**
/// - Paths beginning with the home directory or with a git repo right
/// inside the home directory will be contracted to `~`
/// - Paths containing a git repo will contract to begin at the repo root
///
/// **Substitution**
/// Paths will undergo user-provided substitutions of substrings
///
/// **Truncation**
/// Paths will be limited in length to `3` path components by default.
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
@ -67,10 +71,14 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
_ => contract_path(current_dir, &home_dir, HOME_SYMBOL),
};
// Truncate the dir string to the maximum number of path components
let truncated_dir_string = truncate(dir_string, config.truncation_length as usize);
let substituted_dir = substitute_path(dir_string, &config.substitutions);
if config.fish_style_pwd_dir_length > 0 {
// Truncate the dir string to the maximum number of path components
let truncated_dir_string = truncate(substituted_dir, config.truncation_length as usize);
// Substitutions could have changed the prefix, so don't allow them and
// fish-style path contraction together
if config.fish_style_pwd_dir_length > 0 && config.substitutions.is_empty() {
// If user is using fish style path, we need to add the segment first
let contracted_home_dir = contract_path(&current_dir, &home_dir, HOME_SYMBOL);
let fish_style_dir = to_fish_style(
@ -126,6 +134,18 @@ fn contract_path(full_path: &Path, top_level_path: &Path, top_level_replacement:
)
}
/// Perform a list of string substitutions on the path
///
/// Given a list of (from, to) pairs, this will perform the string
/// substitutions, in order, on the path. Any non-pair of strings is ignored.
fn substitute_path(dir_string: String, substitutions: &HashMap<String, &str>) -> String {
let mut substituted_dir = dir_string;
for substitution_pair in substitutions.iter() {
substituted_dir = substituted_dir.replace(substitution_pair.0, substitution_pair.1);
}
substituted_dir
}
/// Takes part before contracted path and replaces it with fish style path
///
/// Will take the first letter of each directory before the contracted path and
@ -223,6 +243,17 @@ mod tests {
assert_eq!(output, "C:/");
}
#[test]
fn substitute_prefix_and_middle() {
let full_path = "/absolute/path/foo/bar/baz";
let mut substitutions = HashMap::new();
substitutions.insert("/absolute/path".to_string(), "");
substitutions.insert("/bar/".to_string(), "/");
let output = substitute_path(full_path.to_string(), &substitutions);
assert_eq!(output, "/foo/baz");
}
#[test]
fn fish_style_with_user_home_contracted_path() {
let path = "~/starship/engines/booster/rocket";

View File

@ -24,6 +24,50 @@ fn home_directory() -> io::Result<()> {
Ok(())
}
#[test]
fn substituted_truncated_path() -> io::Result<()> {
let output = common::render_module("directory")
.arg("--path=/some/long/network/path/workspace/a/b/c/dev")
.use_config(toml::toml! {
[directory]
truncation_length = 4
[directory.substitutions]
"/some/long/network/path" = "/some/net"
"a/b/c" = "d"
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("in {} ", Color::Cyan.bold().paint("net/workspace/d/dev"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn strange_substitution() -> io::Result<()> {
let strange_sub = "/\\/;,!";
let output = common::render_module("directory")
.arg("--path=/foo/bar/regular/path")
.use_config(toml::toml! {
[directory]
truncation_length = 0
fish_style_pwd_dir_length = 2 // Overridden by substitutions
[directory.substitutions]
"regular" = strange_sub
})
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"in {} ",
Color::Cyan
.bold()
.paint(format!("/foo/bar/{}/path", strange_sub))
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn directory_in_home() -> io::Result<()> {