diff options
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/aws.rs | 29 | ||||
-rw-r--r-- | src/modules/battery.rs | 137 | ||||
-rw-r--r-- | src/modules/character.rs | 69 | ||||
-rw-r--r-- | src/modules/cmd_duration.rs | 101 | ||||
-rw-r--r-- | src/modules/directory.rs | 312 | ||||
-rw-r--r-- | src/modules/env_var.rs | 43 | ||||
-rw-r--r-- | src/modules/git_branch.rs | 65 | ||||
-rw-r--r-- | src/modules/git_state.rs | 162 | ||||
-rw-r--r-- | src/modules/git_status.rs | 196 | ||||
-rw-r--r-- | src/modules/golang.rs | 82 | ||||
-rw-r--r-- | src/modules/hostname.rs | 41 | ||||
-rw-r--r-- | src/modules/java.rs | 105 | ||||
-rw-r--r-- | src/modules/jobs.rs | 34 | ||||
-rw-r--r-- | src/modules/line_break.rs | 15 | ||||
-rw-r--r-- | src/modules/memory_usage.rs | 91 | ||||
-rw-r--r-- | src/modules/mod.rs | 63 | ||||
-rw-r--r-- | src/modules/nix_shell.rs | 56 | ||||
-rw-r--r-- | src/modules/nodejs.rs | 49 | ||||
-rw-r--r-- | src/modules/package.rs | 166 | ||||
-rw-r--r-- | src/modules/python.rs | 122 | ||||
-rw-r--r-- | src/modules/ruby.rs | 61 | ||||
-rw-r--r-- | src/modules/rust.rs | 291 | ||||
-rw-r--r-- | src/modules/time.rs | 105 | ||||
-rw-r--r-- | src/modules/username.rs | 53 |
24 files changed, 0 insertions, 2448 deletions
diff --git a/src/modules/aws.rs b/src/modules/aws.rs deleted file mode 100644 index 65ca05142..000000000 --- a/src/modules/aws.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::env; - -use ansi_term::Color; - -use super::{Context, Module}; - -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - const AWS_CHAR: &str = "☁️ "; - const AWS_PREFIX: &str = "on "; - - let aws_profile = env::var("AWS_PROFILE").ok()?; - if aws_profile.is_empty() { - return None; - } - - let mut module = context.new_module("aws"); - - let module_style = module - .config_value_style("style") - .unwrap_or_else(|| Color::Yellow.bold()); - module.set_style(module_style); - - module.get_prefix().set_value(AWS_PREFIX); - - module.new_segment("symbol", AWS_CHAR); - module.new_segment("profile", &aws_profile); - - Some(module) -} diff --git a/src/modules/battery.rs b/src/modules/battery.rs deleted file mode 100644 index 04b5a2d8a..000000000 --- a/src/modules/battery.rs +++ /dev/null @@ -1,137 +0,0 @@ -use ansi_term::{Color, Style}; - -use super::{Context, Module}; -use crate::config::Config; - -/// Creates a module for the battery percentage and charging state -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - const BATTERY_FULL: &str = "•"; - const BATTERY_CHARGING: &str = "⇡"; - const BATTERY_DISCHARGING: &str = "⇣"; - // TODO: Update when v1.0 printing refactor is implemented to only - // print escapes in a prompt context. - let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default(); - let percentage_char = match shell.as_str() { - "zsh" => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc` - _ => "%", - }; - - let battery_status = get_battery_status()?; - let BatteryStatus { state, percentage } = battery_status; - - let mut module = context.new_module("battery"); - - // Parse config under `display` - let display_styles = get_display_styles(&module); - let display_style = display_styles.iter().find(|display_style| { - let BatteryDisplayStyle { threshold, .. } = display_style; - percentage <= *threshold as f32 - }); - - if let Some(display_style) = display_style { - let BatteryDisplayStyle { style, .. } = display_style; - - // Set style based on percentage - module.set_style(*style); - module.get_prefix().set_value(""); - - match state { - battery::State::Full => { - module.new_segment("full_symbol", BATTERY_FULL); - } - battery::State::Charging => { - module.new_segment("charging_symbol", BATTERY_CHARGING); - } - battery::State::Discharging => { - module.new_segment("discharging_symbol", BATTERY_DISCHARGING); - } - battery::State::Unknown => { - log::debug!("Unknown detected"); - module.new_segment_if_config_exists("unknown_symbol")?; - } - battery::State::Empty => { - module.new_segment_if_config_exists("empty_symbol")?; - } - _ => { - log::debug!("Unhandled battery state `{}`", state); - return None; - } - } - - let mut percent_string = Vec::<String>::with_capacity(2); - // Round the percentage to a whole number - percent_string.push(percentage.round().to_string()); - percent_string.push(percentage_char.to_string()); - module.new_segment("percentage", percent_string.join("").as_ref()); - - Some(module) - } else { - None - } -} - -fn get_display_styles(module: &Module) -> Vec<BatteryDisplayStyle> { - if let Some(display_configs) = module.config_value_array("display") { - let mut display_styles: Vec<BatteryDisplayStyle> = vec![]; - for display_config in display_configs.iter() { - if let toml::Value::Table(config) = display_config { - if let Some(display_style) = BatteryDisplayStyle::from_config(config) { - display_styles.push(display_style); - } - } - } - - // Return display styles as long as display array exists, even if it is empty. - display_styles - } else { - // Default display styles: [{ threshold = 10, style = "red bold" }] - vec![BatteryDisplayStyle { - threshold: 10, - style: Color::Red.bold(), - }] - } -} - -fn get_battery_status() -> Option<BatteryStatus> { - let battery_manager = battery::Manager::new().ok()?; - match battery_manager.batteries().ok()?.next() { - Some(Ok(battery)) => { - log::debug!("Battery found: {:?}", battery); - let battery_status = BatteryStatus { - percentage: battery.state_of_charge().value * 100.0, - state: battery.state(), - }; - - Some(battery_status) - } - Some(Err(e)) => { - log::debug!("Unable to access battery information:\n{}", &e); - None - } - None => { - log::debug!("No batteries found"); - None - } - } -} - -struct BatteryStatus { - percentage: f32, - state: battery::State, -} - -#[derive(Clone, Debug)] -struct BatteryDisplayStyle { - threshold: i64, - style: Style, -} - -impl BatteryDisplayStyle { - /// construct battery display style from toml table - pub fn from_config(config: &toml::value::Table) -> Option<BatteryDisplayStyle> { - let threshold = config.get_as_i64("threshold")?; - let style = config.get_as_ansi_style("style")?; - - Some(BatteryDisplayStyle { threshold, style }) - } -} diff --git a/src/modules/character.rs b/src/modules/character.rs deleted file mode 100644 index 9385e53c9..000000000 --- a/src/modules/character.rs +++ /dev/null @@ -1,69 +0,0 @@ -use super::{Context, Module}; -use ansi_term::Color; - -/// Creates a module for the prompt character -/// -/// The character segment prints an arrow character in a color dependant on the exit- -/// code of the last executed command: -/// - If the exit-code was "0", the arrow will be formatted with `style_success` -/// (green by default) -/// - If the exit-code was anything else, the arrow will be formatted with -/// `style_failure` (red by default) -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - const SUCCESS_CHAR: &str = "❯"; - const FAILURE_CHAR: &str = "✖"; - const VICMD_CHAR: &str = "❮"; - enum ShellEditMode { - Normal, - Insert, - }; - const ASSUMED_MODE: ShellEditMode = ShellEditMode::Insert; - // TODO: extend config to more modes - - let mut module = context.new_module("character"); - module.get_prefix().set_value(""); - - let style_success = module - .config_value_style("style_success") - .unwrap_or_else(|| Color::Green.bold()); - let style_failure = module - .config_value_style("style_failure") - .unwrap_or_else(|| Color::Red.bold()); - - let arguments = &context.arguments; - let use_symbol = module - .config_value_bool("use_symbol_for_status") - .unwrap_or(false); - let exit_success = arguments.value_of("status_code").unwrap_or("0") == "0"; - let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default(); - let keymap = arguments.value_of("keymap").unwrap_or("viins"); - - // Match shell "keymap" names to normalized vi modes - // NOTE: in vi mode, fish reports normal mode as "default". - // Unfortunately, this is also the name of the non-vi default mode. - // We do some environment detection in src/init.rs to translate. - // The result: in non-vi fish, keymap is always reported as "insert" - let mode = match (shell.as_str(), keymap) { - ("fish", "default") | ("zsh", "vicmd") => ShellEditMode::Normal, - _ => ASSUMED_MODE, - }; - - /* If an error symbol is set in the config, use symbols to indicate - success/failure, in addition to color */ - let symbol = if use_symbol && !exit_success { - module.new_segment("error_symbol", FAILURE_CHAR) - } else { - match mode { - ShellEditMode::Normal => module.new_segment("vicmd_symbol", VICMD_CHAR), - ShellEditMode::Insert => module.new_segment("symbol", SUCCESS_CHAR), - } - }; - - if exit_success { - symbol.set_style(style_success); - } else { - symbol.set_style(style_failure); - }; - - Some(module) -} diff --git a/src/modules/cmd_duration.rs b/src/modules/cmd_duration.rs deleted file mode 100644 index 8d4816392..000000000 --- a/src/modules/cmd_duration.rs +++ /dev/null @@ -1,101 +0,0 @@ -use ansi_term::Color; - -use super::{Context, Module}; - -/// Outputs the time it took the last command to execute -/// -/// Will only print if last command took more than a certain amount of time to -/// execute. Default is two seconds, but can be set by config option `min_time`. -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - let mut module = context.new_module("cmd_duration"); - - let arguments = &context.arguments; - let elapsed = arguments - .value_of("cmd_duration") - .unwrap_or("invalid_time") - .parse::<u64>() - .ok()?; - - let prefix = module - .config_value_str("prefix") - .unwrap_or("took ") - .to_owned(); - - let signed_config_min = module.config_value_i64("min_time").unwrap_or(2); - - /* TODO: Once error handling is implemented, warn the user if their config - min time is nonsensical */ - if signed_config_min < 0 { - log::debug!( - "[WARN]: min_time in [cmd_duration] ({}) was less than zero", - signed_config_min - ); - return None; - } - - let config_min = signed_config_min as u64; - - let module_color = match elapsed { - time if time < config_min => return None, - _ => module - .config_value_style("style") - .unwrap_or_else(|| Color::Yellow.bold()), - }; - - module.set_style(module_color); - module.new_segment( - "cmd_duration", - &format!("{}{}", prefix, render_time(elapsed)), - ); - module.get_prefix().set_value(""); - - Some(module) -} - -// Render the time into a nice human-readable string -fn render_time(raw_seconds: u64) -> String { - // Calculate a simple breakdown into days/hours/minutes/seconds - let (seconds, raw_minutes) = (raw_seconds % 60, raw_seconds / 60); - let (minutes, raw_hours) = (raw_minutes % 60, raw_minutes / 60); - let (hours, days) = (raw_hours % 24, raw_hours / 24); - - let components = [days, hours, minutes, seconds]; - let suffixes = ["d", "h", "m", "s"]; - - let rendered_components: Vec<String> = components - .iter() - .zip(&suffixes) - .map(render_time_component) - .collect(); - rendered_components.join("") -} - -/// Render a single component of the time string, giving an empty string if component is zero -fn render_time_component((component, suffix): (&u64, &&str)) -> String { - match component { - 0 => String::new(), - n => format!("{}{}", n, suffix), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_10s() { - assert_eq!(render_time(10 as u64), "10s") - } - #[test] - fn test_90s() { - assert_eq!(render_time(90 as u64), "1m30s") - } - #[test] - fn test_10110s() { - assert_eq!(render_time(10110 as u64), "2h48m30s") - } - #[test] - fn test_1d() { - assert_eq!(render_time(86400 as u64), "1d") - } -} diff --git a/src/modules/directory.rs b/src/modules/directory.rs deleted file mode 100644 index 4bab47c71..000000000 --- a/src/modules/directory.rs +++ /dev/null @@ -1,312 +0,0 @@ -use ansi_term::Color; -use path_slash::PathExt; -use std::path::Path; - -use super::{Context, Module}; - -/// Creates a module with the current directory -/// -/// Will perform path contraction 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 -/// -/// **Truncation** -/// Paths will be limited in length to `3` path components by default. -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 mut module = context.new_module("directory"); - let module_color = module - .config_value_style("style") - .unwrap_or_else(|| Color::Cyan.bold()); - module.set_style(module_color); - - let truncation_length = module - .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); - - // Using environment PWD is the standard approach for determining logical path - let use_logical_path = module.config_value_bool("use_logical_path").unwrap_or(true); - // If this is None for any reason, we fall back to reading the os-provided path - let logical_current_dir = if use_logical_path { - match std::env::var("PWD") { - Ok(x) => Some(x), - Err(_) => { - log::debug!("Asked for logical path, but PWD was invalid."); - None - } - } - } else { - None - }; - let current_dir = logical_current_dir - .as_ref() - .map(|d| Path::new(d)) - .unwrap_or_else(|| context.current_dir.as_ref()); - - let home_dir = dirs::home_dir().unwrap(); - log::debug!("Current directory: {:?}", current_dir); - - let repo = &context.get_repo().ok()?; - - let dir_string = match &repo.root { - Some(repo_root) if truncate_to_repo && (repo_root != &home_dir) => { - let repo_folder_name = repo_root.file_name().unwrap().to_str().unwrap(); - - // 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(¤t_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 "); - - Some(module) -} - -/// Contract the root component of a path -/// -/// Replaces the `top_level_path` in a given `full_path` with the provided -/// `top_level_replacement`. -fn contract_path(full_path: &Path, top_level_path: &Path, top_level_replacement: &str) -> String { - if !full_path.starts_with(top_level_path) { - return replace_c_dir(full_path.to_slash().unwrap()); - } - - if full_path == top_level_path { - return replace_c_dir(top_level_replacement.to_string()); - } - - format!( - "{replacement}{separator}{path}", - replacement = top_level_replacement, - separator = "/", - path = replace_c_dir( - full_path - .strip_prefix(top_level_path) - .unwrap() - .to_slash() - .unwrap() - ) - ) -} - -/// Replaces "C://" with "/c/" within a Windows path -/// -/// On non-Windows OS, does nothing -#[cfg(target_os = "windows")] -fn replace_c_dir(path: String) -> String { - return path.replace("C:/", "/c"); -} - -/// Replaces "C://" with "/c/" within a Windows path -/// -/// On non-Windows OS, does nothing -#[cfg(not(target_os = "windows"))] -const fn replace_c_dir(path: String) -> String { - path -} - -/// Truncate a path to only have a set number of path components -/// -/// Will truncate a path to only show the last `length` components in a path. -/// If a length of `0` is provided, the path will not be truncated. -fn truncate(dir_string: String, length: usize) -> String { - if length == 0 { - return dir_string; - } - - let components = dir_string.split('/').collect::<Vec<&str>>(); - if components.len() <= length { - return dir_string; - } - - let truncated_components = &components[components.len() - length..]; - 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.trim_end_matches(truncated_dir_string).to_owned(); - 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::*; - - #[test] - fn contract_home_directory() { - let full_path = Path::new("/Users/astronaut/schematics/rocket"); - let home = Path::new("/Users/astronaut"); - - let output = contract_path(full_path, home, "~"); - assert_eq!(output, "~/schematics/rocket"); - } - - #[test] - fn contract_repo_directory() { - let full_path = Path::new("/Users/astronaut/dev/rocket-controls/src"); - let repo_root = Path::new("/Users/astronaut/dev/rocket-controls"); - - let output = contract_path(full_path, repo_root, "rocket-controls"); - assert_eq!(output, "rocket-controls/src"); - } - - #[test] - #[cfg(target_os = "windows")] - fn contract_windows_style_home_directory() { - let full_path = Path::new("C:\\Users\\astronaut\\schematics\\rocket"); - let home = Path::new("C:\\Users\\astronaut"); - - let output = contract_path(full_path, home, "~"); - assert_eq!(output, "~/schematics/rocket"); - } - - #[test] - #[cfg(target_os = "windows")] - fn contract_windows_style_repo_directory() { - let full_path = Path::new("C:\\Users\\astronaut\\dev\\rocket-controls\\src"); - let repo_root = Path::new("C:\\Users\\astronaut\\dev\\rocket-controls"); - - let output = contract_path(full_path, repo_root, "rocket-controls"); - assert_eq!(output, "rocket-controls/src"); - } - - #[test] - #[cfg(target_os = "windows")] - fn contract_windows_style_no_top_level_directory() { - let full_path = Path::new("C:\\Some\\Other\\Path"); - let top_level_path = Path::new("C:\\Users\\astronaut"); - - let output = contract_path(full_path, top_level_path, "~"); - assert_eq!(output, "/c/Some/Other/Path"); - } - - #[test] - #[cfg(target_os = "windows")] - fn contract_windows_style_root_directory() { - let full_path = Path::new("C:\\"); - let top_level_path = Path::new("C:\\Users\\astronaut"); - - let output = contract_path(full_path, top_level_path, "~"); - assert_eq!(output, "/c"); - } - - #[test] - fn truncate_smaller_path_than_provided_length() { - let path = "~/starship"; - let output = truncate(path.to_string(), 3); - assert_eq!(output, "~/starship") - } - - #[test] - fn truncate_same_path_as_provided_length() { - let path = "~/starship/engines"; - let output = truncate(path.to_string(), 3); - assert_eq!(output, "~/starship/engines") - } - - #[test] - fn truncate_slightly_larger_path_than_provided_length() { - let path = "~/starship/engines/booster"; - let output = truncate(path.to_string(), 3); - assert_eq!(output, "starship/engines/booster") - } - - #[test] - fn truncate_larger_path_than_provided_length() { - let path = "~/starship/engines/booster/rocket"; - 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/"); - } - - #[test] - fn fish_style_with_duplicate_directories() { - let path = "~/starship/tmp/C++/C++/C++"; - let output = to_fish_style(1, path.to_string(), "C++"); - assert_eq!(output, "~/s/t/C/C/"); - } -} diff --git a/src/modules/env_var.rs b/src/modules/env_var.rs deleted file mode 100644 index bb46456a1..000000000 --- a/src/modules/env_var.rs +++ /dev/null @@ -1,43 +0,0 @@ -use ansi_term::Color; -use std::env; - -use super::{Context, Module}; - -/// Creates a module with the value of the chosen environment variable -/// -/// Will display the environment variable's value if all of the following criteria are met: -/// - env_var.disabled is absent or false -/// - env_var.variable is defined -/// - a variable named as the value of env_var.variable is defined -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - let mut module = context.new_module("env_var"); - let module_style = module - .config_value_style("style") - .unwrap_or_else(|| Color::Black.bold().dimmed()); - - let env_name = module.config_value_str("variable")?; - - let default_value = module.config_value_str("default"); - - let env_value = get_env_value(env_name, default_value)?; - - let prefix = module.config_value_str("prefix").unwrap_or("").to_owned(); - let suffix = module.config_value_str("suffix").unwrap_or("").to_owned(); - - module.set_style(module_style); - module.get_prefix().set_value("with "); - module.new_segment_if_config_exists("symbol"); - module.new_segment("env_var", &format!("{}{}{}", prefix, env_value, suffix)); - - Some(module) -} - -fn get_env_value(name: &str, default: Option<&str>) -> Option<String> { - match env::var_os(name) { - Some(os_value) => match os_value.into_string() { - Ok(value) => Some(value), - Err(_error) => None, - }, - None => default.map(|value| value.to_owned()), - } -} diff --git a/src/modules/git_branch.rs b/src/modules/git_branch.rs deleted file mode 100644 index 6d07e8905..000000000 --- a/src/modules/git_branch.rs +++ /dev/null @@ -1,65 +0,0 @@ -use ansi_term::Color; -use unicode_segmentation::UnicodeSegmentation; - -use super::{Context, Module}; - -/// Creates a module with the Git branch in the current directory -/// -/// Will display the branch name if the current directory is a git repo -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - const GIT_BRANCH_CHAR: &str = " "; - - let mut module = context.new_module("git_branch"); - - let segment_color = module - .config_value_style("style") - .unwrap_or_else(|| Color::Purple.bold()); - module.set_style(segment_color); - module.get_prefix().set_value("on "); - - let unsafe_truncation_length = module - .config_value_i64("truncation_length") - .unwrap_or(std::i64::MAX); - let truncation_symbol = get_graphemes( - module.config_value_str("truncation_symbol").unwrap_or("…"), - 1, - ); - - module.new_segment("symbol", GIT_BRANCH_CHAR); - - // TODO: Once error handling is implemented, warn the user if their config - // truncation length is nonsensical - let len = if unsafe_truncation_length <= 0 { - log::debug!( - "[WARN]: \"truncation_length\" should be a positive value, found {}", - unsafe_truncation_length - ); - std::usize::MAX - } else { - unsafe_truncation_length as usize - }; - let repo = context.get_repo().ok()?; - let branch_name = repo.branch.as_ref()?; - let truncated_graphemes = get_graphemes(&branch_name, len); - // The truncation symbol should only be added if we truncated - let truncated_and_symbol = if len < graphemes_len(&branch_name) { - truncated_graphemes + &truncation_symbol - } else { - truncated_graphemes - }; - - module.new_segment("name", &truncated_and_symbol); - - Some(module) -} - -fn get_graphemes(text: &str, length: usize) -> String { - UnicodeSegmentation::graphemes(text, true) - .take(length) - .collect::<Vec<&str>>() - .concat() -} - -fn graphemes_len(text: &str) -> usize { - UnicodeSegmentation::graphemes(&text[..], true).count() -} diff --git a/src/modules/git_state.rs b/src/modules/git_state.rs deleted file mode 100644 index c26a6007b..000000000 --- a/src/modules/git_state.rs +++ /dev/null @@ -1,162 +0,0 @@ -use ansi_term::Color; -use git2::RepositoryState; -use std::path::{Path, PathBuf}; - -use super::{Context, Module}; - -/// Creates a module with the state of the git repository at the current directory -/// -/// During a git operation it will show: REBASING, BISECTING, MERGING, etc. -/// If the progress information is available (e.g. rebasing 3/10), it will show that too. -pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { - let mut module = context.new_module("git_state"); - - let repo = context.get_repo().ok()?; - let repo_root = repo.root.as_ref()?; - let repo_state = repo.state?; - let state_description = get_state_description(repo_state, repo_root); - - if let StateDescription::Clean = state_description { - return None; - } - - let module_style = module - .config_value_style("style") - .unwrap_or_else(|| Color::Yellow.bold()); - module.set_style(module_style); - module.get_prefix().set_value("("); - module.get_suffix().set_value(") "); - - let label = match state_description { - StateDescription::Label(label) => label, - StateDescription::LabelAndProgress(label, _) => label, - // Should only be possible if you've added a new variant to StateDescription - _ => panic!("Expected to have a label at this point in the control flow."), - }; - - module.new_segment(label.segment_name, label.message_default); - - if let StateDescription::LabelAndProgress(_, progress) = state_description { - module.new_segment("progress_current", &format!(" {}", progress.current)); - module.new_segment("progress_divider", "/"); - module.new_segment("progress_total", &format!("{}", progress.total)); - } - - Some(module) -} - -static MERGE_LABEL: StateLabel = StateLabel { - segment_name: "merge", - message_default: "MERGING", -}; - -static REVERT_LABEL: StateLabel = StateLabel { - segment_name: "revert", - message_default: "REVERTING", -}; - -static CHERRY_LABEL: StateLabel = StateLabel { - segment_name: "cherry_pick", - message_default: "CHERRY-PICKING", -}; - -static BISECT_LABEL: StateLabel = StateLabel { - segment_name: "bisect", - message_default: "BISECTING", -}; - -static AM_LABEL: StateLabel = StateLabel { - segment_name: "am", - message_default: "AM", -}; - -static REBASE_LABEL: StateLabel = StateLabel { - segment_name: "rebase", - message_default: "REBASING", -}; - -static AM_OR_REBASE_LABEL: StateLabel = StateLabel { - segment_name: "am_or_rebase", - message_default: "AM/REBASE", -}; - -/// Returns the state of the current repository -/// -/// During a git operation it will show: REBASING, BISECTING, MERGING, etc. -fn get_state_description(state: RepositoryState, root: &Path |