diff options
author | Jon Grythe Stødle <jonstodle@outlook.com> | 2020-01-02 05:19:08 +0100 |
---|---|---|
committer | Matan Kushner <hello@matchai.me> | 2020-01-01 23:19:08 -0500 |
commit | 8e5fa60fc8dbea2274284b120fdb454bbfea2fc9 (patch) | |
tree | 6dc0df93bbb1b99a9d682c222b4e99749044c3a0 /src/print.rs | |
parent | 6bafe4cd66ef89b309da4c6b280a56c41a56e74e (diff) |
feat: Add the `starship explain` command (#699)
This adds the explain argument to Starship, which explains what the printed modules in the prompt are.
Diffstat (limited to 'src/print.rs')
-rw-r--r-- | src/print.rs | 119 |
1 files changed, 98 insertions, 21 deletions
diff --git a/src/print.rs b/src/print.rs index 70826827f..ee3b36662 100644 --- a/src/print.rs +++ b/src/print.rs @@ -2,6 +2,7 @@ use clap::ArgMatches; use rayon::prelude::*; use std::fmt::Write as FmtWrite; use std::io::{self, Write}; +use unicode_width::UnicodeWidthChar; use crate::context::Context; use crate::module::Module; @@ -26,27 +27,7 @@ pub fn get_prompt(context: Context) -> String { buf.push_str("\x1b[J"); - let mut prompt_order: Vec<&str> = Vec::new(); - - // Write out a custom prompt order - for module in config.prompt_order { - if ALL_MODULES.contains(&module) { - prompt_order.push(module); - } else { - log::debug!( - "Expected prompt_order to contain value from {:?}. Instead received {}", - ALL_MODULES, - module, - ); - } - } - - let modules = &prompt_order - .par_iter() - .filter(|module| !context.is_module_disabled_in_config(module)) - .map(|module| modules::handle(module, &context)) // Compute modules - .flatten() - .collect::<Vec<Module>>(); // Remove segments set to `None` + let modules = compute_modules(&context); let mut print_without_prefix = true; let printable = modules.iter(); @@ -76,3 +57,99 @@ pub fn module(module_name: &str, args: ArgMatches) { print!("{}", module); } + +pub fn explain(args: ArgMatches) { + let context = Context::new(args); + + struct ModuleInfo { + value: String, + value_len: usize, + desc: String, + } + + let dont_print = vec!["line_break", "character"]; + + let modules = compute_modules(&context) + .into_iter() + .filter(|module| !dont_print.contains(&module.get_name().as_str())) + .map(|module| { + let ansi_strings = module.ansi_strings_for_prompt(false); + let value = module.get_segments().join(""); + ModuleInfo { + value: ansi_term::ANSIStrings(&ansi_strings[1..ansi_strings.len() - 1]).to_string(), + value_len: value.chars().count() + count_wide_chars(&value), + desc: module.get_description().to_owned(), + } + }) + .collect::<Vec<ModuleInfo>>(); + + let mut max_ansi_module_width = 0; + let mut max_module_width = 0; + + for info in &modules { + max_ansi_module_width = std::cmp::max( + max_ansi_module_width, + info.value.chars().count() + count_wide_chars(&info.value), + ); + max_module_width = std::cmp::max(max_module_width, info.value_len); + } + + let desc_width = term_size::dimensions() + .map(|(w, _)| w) + .map(|width| width - std::cmp::min(width, max_ansi_module_width)); + + println!("\n Here's a breakdown of your prompt:"); + for info in modules { + let wide_chars = count_wide_chars(&info.value); + + if let Some(desc_width) = desc_width { + let wrapped = textwrap::fill(&info.desc, desc_width); + let mut lines = wrapped.split('\n'); + println!( + " {:width$} - {}", + info.value, + lines.next().unwrap(), + width = max_ansi_module_width - wide_chars + ); + + for line in lines { + println!("{}{}", " ".repeat(max_module_width + 6), line.trim()); + } + } else { + println!( + " {:width$} - {}", + info.value, + info.desc, + width = max_ansi_module_width - wide_chars + ); + }; + } +} + +fn compute_modules<'a>(context: &'a Context) -> Vec<Module<'a>> { + let mut prompt_order: Vec<&str> = Vec::new(); + + // Write out a custom prompt order + for module in context.config.get_root_config().prompt_order { + if ALL_MODULES.contains(&module) { + prompt_order.push(module); + } else { + log::debug!( + "Expected prompt_order to contain value from {:?}. Instead received {}", + ALL_MODULES, + module, + ); + } + } + + prompt_order + .par_iter() + .filter(|module| !context.is_module_disabled_in_config(module)) + .map(|module| modules::handle(module, &context)) // Compute modules + .flatten() // Remove segments set to `None` + .collect::<Vec<Module<'a>>>() +} + +fn count_wide_chars(value: &str) -> usize { + value.chars().filter(|c| c.width().unwrap_or(0) > 1).count() +} |