summaryrefslogtreecommitdiffstats
path: root/src/print.rs
diff options
context:
space:
mode:
authorJon Grythe Stødle <jonstodle@outlook.com>2020-01-02 05:19:08 +0100
committerMatan Kushner <hello@matchai.me>2020-01-01 23:19:08 -0500
commit8e5fa60fc8dbea2274284b120fdb454bbfea2fc9 (patch)
tree6dc0df93bbb1b99a9d682c222b4e99749044c3a0 /src/print.rs
parent6bafe4cd66ef89b309da4c6b280a56c41a56e74e (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.rs119
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()
+}