1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-05-28 14:10:53 +00:00

feat: Add ability to use an alternate directory truncation style (#239)

* Add ability to use an alternate directory truncation style
This commit is contained in:
Neil Kistner 2019-08-26 20:52:45 -05:00 committed by Matan Kushner
parent 81ea165cec
commit e034253a5e
3 changed files with 231 additions and 14 deletions

View File

@ -178,13 +178,22 @@ The `directory` module shows the path to your current directory, truncated to
three parent folders. Your directory will also be truncated to the root of the
git repo that you're currently in.
When using the fish style pwd option, instead of hiding the path that is
truncated, you will see a shortened name of each directory based on the number
you enable for the option.
For example, given `~/Dev/Nix/nixpkgs/pkgs` where `nixpkgs` is the repo root,
and the option set to `1`. You will now see `~/D/N/nixpkgs/pkgs`, whereas before
it would have been `nixpkgs/pkgs`.
### Options
| Variable | Default | Description |
| ------------------- | ------- | -------------------------------------------------------------------------------- |
| `truncation_length` | `3` | The number of parent folders that the current directory should be truncated to. |
| `truncate_to_repo` | `true` | Whether or not to truncate to the root of the git repo that you're currently in. |
| `disabled` | `false` | Disables the `directory` module. |
| `truncation_length` | `3` | The number of parent folders that the current directory should be truncated to. |
| `truncate_to_repo` | `true` | Whether or not to truncate to the root of the git repo that you're currently in. |
| `disabled` | `false` | Disables the `directory` module. |
| `fish_style_pwd_dir_length` | `0` | The number of characters to use when applying fish shell pwd path logic. |
### Example
@ -515,4 +524,4 @@ disabled = true
use_name = true
impure_msg = "impure shell"
pure_msg = "pure shell"
```
```

View File

@ -16,6 +16,7 @@ use super::{Context, Module};
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
const HOME_SYMBOL: &str = "~";
const DIR_TRUNCATION_LENGTH: i64 = 3;
const FISH_STYLE_PWD_DIR_LENGTH: i64 = 0;
let module_color = Color::Cyan.bold();
let mut module = context.new_module("directory")?;
@ -25,28 +26,40 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
.config_value_i64("truncation_length")
.unwrap_or(DIR_TRUNCATION_LENGTH);
let truncate_to_repo = module.config_value_bool("truncate_to_repo").unwrap_or(true);
let fish_style_pwd_dir_length = module
.config_value_i64("fish_style_pwd_dir_length")
.unwrap_or(FISH_STYLE_PWD_DIR_LENGTH);
let home_dir = dirs::home_dir().unwrap();
let current_dir = &context.current_dir;
log::debug!("Current directory: {:?}", current_dir);
let dir_string;
match &context.repo_root {
let dir_string = match &context.repo_root {
Some(repo_root) if truncate_to_repo => {
// Contract the path to the git repo root
let repo_folder_name = repo_root.file_name().unwrap().to_str().unwrap();
dir_string = contract_path(current_dir, repo_root, repo_folder_name);
}
_ => {
// Contract the path to the home directory
let home_dir = dirs::home_dir().unwrap();
dir_string = contract_path(current_dir, &home_dir, HOME_SYMBOL);
// Contract the path to the git repo root
contract_path(current_dir, repo_root, repo_folder_name)
}
// Contract the path to the home directory
_ => 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, truncation_length as usize);
if fish_style_pwd_dir_length > 0 {
// 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(
fish_style_pwd_dir_length as usize,
contracted_home_dir,
&truncated_dir_string,
);
module.new_segment("path", &fish_style_dir);
}
module.new_segment("path", &truncated_dir_string);
module.get_prefix().set_value("in ");
@ -115,6 +128,38 @@ fn truncate(dir_string: String, length: usize) -> String {
truncated_components.join("/")
}
/// 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
/// use that in the path instead. See the following example.
///
/// Absolute Path: `/Users/Bob/Projects/work/a_repo`
/// Contracted Path: `a_repo`
/// With Fish Style: `~/P/w/a_repo`
///
/// Absolute Path: `/some/Path/not/in_a/repo/but_nested`
/// Contracted Path: `in_a/repo/but_nested`
/// With Fish Style: `/s/P/n/in_a/repo/but_nested`
fn to_fish_style(pwd_dir_length: usize, dir_string: String, truncated_dir_string: &str) -> String {
let replaced_dir_string = dir_string.replace(truncated_dir_string, "");
let components = replaced_dir_string.split('/').collect::<Vec<&str>>();
if components.is_empty() {
return replaced_dir_string;
}
components
.into_iter()
.map(|word| match word {
"" => "",
_ if word.len() <= pwd_dir_length => word,
_ if word.starts_with('.') => &word[..=pwd_dir_length],
_ => &word[..pwd_dir_length],
})
.collect::<Vec<_>>()
.join("/")
}
#[cfg(test)]
mod tests {
use super::*;
@ -204,4 +249,34 @@ mod tests {
let output = truncate(path.to_string(), 3);
assert_eq!(output, "engines/booster/rocket")
}
#[test]
fn fish_style_with_user_home_contracted_path() {
let path = "~/starship/engines/booster/rocket";
let output = to_fish_style(1, path.to_string(), "engines/booster/rocket");
assert_eq!(output, "~/s/");
}
#[test]
fn fish_style_with_user_home_contracted_path_and_dot_dir() {
let path = "~/.starship/engines/booster/rocket";
let output = to_fish_style(1, path.to_string(), "engines/booster/rocket");
assert_eq!(output, "~/.s/");
}
#[test]
fn fish_style_with_no_contracted_path() {
// `truncatation_length = 2`
let path = "/absolute/Path/not/in_a/repo/but_nested";
let output = to_fish_style(1, path.to_string(), "repo/but_nested");
assert_eq!(output, "/a/P/n/i/");
}
#[test]
fn fish_style_with_pwd_dir_len_no_contracted_path() {
// `truncatation_length = 2`
let path = "/absolute/Path/not/in_a/repo/but_nested";
let output = to_fish_style(2, path.to_string(), "repo/but_nested");
assert_eq!(output, "/ab/Pa/no/in/");
}
}

View File

@ -61,6 +61,28 @@ fn truncated_directory_in_home() -> io::Result<()> {
Ok(())
}
#[test]
#[ignore]
fn fish_directory_in_home() -> io::Result<()> {
let dir = home_dir().unwrap().join("starship/engine/schematics");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 2
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("in {} ", Color::Cyan.bold().paint("~/st/en/schematics"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
fn root_directory() -> io::Result<()> {
let output = common::render_module("directory")
@ -141,6 +163,31 @@ fn truncated_directory_config_large() -> io::Result<()> {
Ok(())
}
#[test]
#[ignore]
fn fish_style_directory_config_large() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 1
fish_style_pwd_dir_length = 100
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"in {} ",
Color::Cyan.bold().paint("/tmp/starship/thrusters/rocket")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn truncated_directory_config_small() -> io::Result<()> {
@ -162,6 +209,28 @@ fn truncated_directory_config_small() -> io::Result<()> {
Ok(())
}
#[test]
#[ignore]
fn fish_directory_config_small() -> io::Result<()> {
let dir = Path::new("/tmp/starship/thrusters/rocket");
fs::create_dir_all(&dir)?;
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
truncation_length = 2
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!("in {} ", Color::Cyan.bold().paint("/t/s/thrusters/rocket"));
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn git_repo_root() -> io::Result<()> {
@ -255,6 +324,70 @@ fn directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
Ok(())
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_false() -> io::Result<()> {
let tmp_dir = TempDir::new_in(dirs::home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// Don't truncate the path at all.
truncation_length = 5
truncate_to_repo = false
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"in {} ",
Color::Cyan
.bold()
.paint("~/.t/above-repo/rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn fish_path_directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {
let tmp_dir = TempDir::new_in(dirs::home_dir().unwrap())?;
let repo_dir = tmp_dir.path().join("above-repo").join("rocket-controls");
let dir = repo_dir.join("src/meters/fuel-gauge");
fs::create_dir_all(&dir)?;
Repository::init(&repo_dir).unwrap();
let output = common::render_module("directory")
.use_config(toml::toml! {
[directory]
// `truncate_to_repo = true` should display the truncated path
truncation_length = 5
truncate_to_repo = true
fish_style_pwd_dir_length = 1
})
.arg("--path")
.arg(dir)
.output()?;
let actual = String::from_utf8(output.stdout).unwrap();
let expected = format!(
"in {} ",
Color::Cyan
.bold()
.paint("~/.t/a/rocket-controls/src/meters/fuel-gauge")
);
assert_eq!(expected, actual);
Ok(())
}
#[test]
#[ignore]
fn directory_in_git_repo_truncate_to_repo_true() -> io::Result<()> {