mirror of
https://github.com/Llewellynvdm/starship.git
synced 2024-11-28 15:56:28 +00:00
feat: refactor modules to use format strings (#1374)
This commit is contained in:
parent
0f52b7b12e
commit
ec76fafff0
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -867,6 +867,15 @@ version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
@ -1151,6 +1160,7 @@ dependencies = [
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"pretty_env_logger",
|
||||
"quick-xml",
|
||||
"rayon",
|
||||
"regex",
|
||||
"remove_dir_all",
|
||||
|
@ -59,6 +59,7 @@ open = "1.4.0"
|
||||
unicode-width = "0.1.8"
|
||||
textwrap = "0.12.1"
|
||||
term_size = "0.3.2"
|
||||
quick-xml = "0.18.1"
|
||||
|
||||
# Optional/http:
|
||||
attohttpc = { version = "0.15.0", optional = true, default-features = false, features = ["tls", "form"] }
|
||||
|
File diff suppressed because it is too large
Load Diff
130
src/config.rs
130
src/config.rs
@ -235,37 +235,84 @@ impl StarshipConfig {
|
||||
|
||||
/// Get the subset of the table for a module by its name
|
||||
pub fn get_module_config(&self, module_name: &str) -> Option<&Value> {
|
||||
let module_config = self.config.as_ref()?.as_table()?.get(module_name);
|
||||
let module_config = self.get_config(&[module_name]);
|
||||
if module_config.is_some() {
|
||||
log::debug!(
|
||||
"Config found for \"{}\": \n{:?}",
|
||||
&module_name,
|
||||
&module_config
|
||||
);
|
||||
} else {
|
||||
log::trace!("No config found for \"{}\"", &module_name);
|
||||
}
|
||||
module_config
|
||||
}
|
||||
|
||||
/// Get the value of the config in a specific path
|
||||
pub fn get_config(&self, path: &[&str]) -> Option<&Value> {
|
||||
let mut prev_table = self.config.as_ref()?.as_table()?;
|
||||
|
||||
assert_ne!(
|
||||
path.len(),
|
||||
0,
|
||||
"Starship::get_config called with an empty path"
|
||||
);
|
||||
|
||||
let (table_options, _) = path.split_at(path.len() - 1);
|
||||
|
||||
// Assumes all keys except the last in path has a table
|
||||
for option in table_options {
|
||||
match prev_table.get(*option) {
|
||||
Some(value) => match value.as_table() {
|
||||
Some(value) => {
|
||||
prev_table = value;
|
||||
}
|
||||
None => {
|
||||
log::trace!(
|
||||
"No config found for \"{}\": \"{}\" is not a table",
|
||||
path.join("."),
|
||||
&option
|
||||
);
|
||||
return None;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
log::trace!(
|
||||
"No config found for \"{}\": Option \"{}\" not found",
|
||||
path.join("."),
|
||||
&option
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let last_option = path.last().unwrap();
|
||||
let value = prev_table.get(*last_option);
|
||||
if value.is_none() {
|
||||
log::trace!(
|
||||
"No config found for \"{}\": Option \"{}\" not found",
|
||||
path.join("."),
|
||||
&last_option
|
||||
);
|
||||
};
|
||||
value
|
||||
}
|
||||
|
||||
/// Get the subset of the table for a custom module by its name
|
||||
pub fn get_custom_module_config(&self, module_name: &str) -> Option<&Value> {
|
||||
let module_config = self.get_custom_modules()?.get(module_name);
|
||||
let module_config = self.get_config(&["custom", module_name]);
|
||||
if module_config.is_some() {
|
||||
log::debug!(
|
||||
"Custom config found for \"{}\": \n{:?}",
|
||||
&module_name,
|
||||
&module_config
|
||||
);
|
||||
} else {
|
||||
log::trace!("No custom config found for \"{}\"", &module_name);
|
||||
}
|
||||
module_config
|
||||
}
|
||||
|
||||
/// Get the table of all the registered custom modules, if any
|
||||
pub fn get_custom_modules(&self) -> Option<&toml::value::Table> {
|
||||
self.config.as_ref()?.as_table()?.get("custom")?.as_table()
|
||||
self.get_config(&["custom"])?.as_table()
|
||||
}
|
||||
|
||||
pub fn get_root_config(&self) -> StarshipRootConfig {
|
||||
@ -277,75 +324,6 @@ impl StarshipConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SegmentConfig<'a> {
|
||||
pub value: &'a str,
|
||||
pub style: Option<Style>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleConfig<'a> for SegmentConfig<'a> {
|
||||
fn from_config(config: &'a Value) -> Option<Self> {
|
||||
match config {
|
||||
Value::String(ref config_str) => Some(Self {
|
||||
value: config_str,
|
||||
style: None,
|
||||
}),
|
||||
Value::Table(ref config_table) => Some(Self {
|
||||
value: config_table.get("value")?.as_str()?,
|
||||
style: config_table.get("style").and_then(<Style>::from_config),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config(&self, config: &'a Value) -> Self {
|
||||
let mut new_config = self.clone();
|
||||
match config {
|
||||
Value::String(ref config_str) => {
|
||||
new_config.value = config_str;
|
||||
}
|
||||
Value::Table(ref config_table) => {
|
||||
if let Some(Value::String(value)) = config_table.get("value") {
|
||||
new_config.value = value;
|
||||
};
|
||||
if let Some(style) = config_table.get("style") {
|
||||
new_config.style = <Style>::from_config(style);
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
new_config
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SegmentConfig<'a> {
|
||||
pub fn new(value: &'a str) -> Self {
|
||||
Self { value, style: None }
|
||||
}
|
||||
|
||||
/// Immutably set value
|
||||
pub fn with_value(&self, value: &'a str) -> Self {
|
||||
Self {
|
||||
value,
|
||||
style: self.style,
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutably set style
|
||||
pub fn with_style(&self, style: Option<Style>) -> Self {
|
||||
Self {
|
||||
value: self.value,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SegmentConfig<'static> {
|
||||
fn default() -> Self {
|
||||
Self::new("")
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a style string which represents an ansi style. Valid tokens in the style
|
||||
string include the following:
|
||||
- 'fg:<color>' (specifies that the color read should be a foreground color)
|
||||
|
@ -1,48 +1,24 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum AwsItems {
|
||||
All,
|
||||
Region,
|
||||
Profile,
|
||||
}
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct AwsConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub profile: SegmentConfig<'a>,
|
||||
pub region: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
pub displayed_items: AwsItems,
|
||||
pub region_aliases: HashMap<String, &'a str>,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for AwsConfig<'a> {
|
||||
fn new() -> Self {
|
||||
AwsConfig {
|
||||
symbol: SegmentConfig::new("☁️ "),
|
||||
profile: SegmentConfig::default(),
|
||||
region: SegmentConfig::default(),
|
||||
style: Color::Yellow.bold(),
|
||||
format: "on [$symbol$profile(\\($region\\))]($style) ",
|
||||
symbol: "☁️ ",
|
||||
style: "bold yellow",
|
||||
disabled: false,
|
||||
displayed_items: AwsItems::All,
|
||||
region_aliases: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ModuleConfig<'a> for AwsItems {
|
||||
fn from_config(config: &toml::Value) -> Option<Self> {
|
||||
match config.as_str()? {
|
||||
"all" => Some(AwsItems::All),
|
||||
"region" => Some(AwsItems::Region),
|
||||
"profile" => Some(AwsItems::Profile),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,39 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct BatteryConfig<'a> {
|
||||
pub full_symbol: SegmentConfig<'a>,
|
||||
pub charging_symbol: SegmentConfig<'a>,
|
||||
pub discharging_symbol: SegmentConfig<'a>,
|
||||
pub unknown_symbol: Option<SegmentConfig<'a>>,
|
||||
pub empty_symbol: Option<SegmentConfig<'a>>,
|
||||
pub display: Vec<BatteryDisplayConfig>,
|
||||
pub full_symbol: &'a str,
|
||||
pub charging_symbol: &'a str,
|
||||
pub discharging_symbol: &'a str,
|
||||
pub unknown_symbol: Option<&'a str>,
|
||||
pub empty_symbol: Option<&'a str>,
|
||||
pub display: Vec<BatteryDisplayConfig<'a>>,
|
||||
pub disabled: bool,
|
||||
pub percentage: SegmentConfig<'a>,
|
||||
pub format: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for BatteryConfig<'a> {
|
||||
fn new() -> Self {
|
||||
BatteryConfig {
|
||||
full_symbol: SegmentConfig::new("•"),
|
||||
charging_symbol: SegmentConfig::new("↑"),
|
||||
discharging_symbol: SegmentConfig::new("↓"),
|
||||
full_symbol: "•",
|
||||
charging_symbol: "↑",
|
||||
discharging_symbol: "↓",
|
||||
unknown_symbol: None,
|
||||
empty_symbol: None,
|
||||
format: "[$symbol$percentage]($style) ",
|
||||
display: vec![BatteryDisplayConfig {
|
||||
threshold: 10,
|
||||
style: Color::Red.bold(),
|
||||
style: "red bold",
|
||||
}],
|
||||
disabled: false,
|
||||
percentage: SegmentConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct BatteryDisplayConfig {
|
||||
pub struct BatteryDisplayConfig<'a> {
|
||||
pub threshold: i64,
|
||||
pub style: Style,
|
||||
pub style: &'a str,
|
||||
}
|
||||
|
@ -1,28 +1,23 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct CharacterConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub error_symbol: SegmentConfig<'a>,
|
||||
pub vicmd_symbol: SegmentConfig<'a>,
|
||||
pub use_symbol_for_status: bool,
|
||||
pub style_success: Style,
|
||||
pub style_failure: Style,
|
||||
pub format: &'a str,
|
||||
pub success_symbol: &'a str,
|
||||
pub error_symbol: &'a str,
|
||||
pub vicmd_symbol: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for CharacterConfig<'a> {
|
||||
fn new() -> Self {
|
||||
CharacterConfig {
|
||||
symbol: SegmentConfig::new("❯"),
|
||||
error_symbol: SegmentConfig::new("✖"),
|
||||
vicmd_symbol: SegmentConfig::new("❮"),
|
||||
use_symbol_for_status: false,
|
||||
style_success: Color::Green.bold(),
|
||||
style_failure: Color::Red.bold(),
|
||||
format: "$symbol ",
|
||||
success_symbol: "[❯](bold green)",
|
||||
error_symbol: "[❯](bold red)",
|
||||
vicmd_symbol: "[❮](bold green)",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct CmdDurationConfig<'a> {
|
||||
pub min_time: i64,
|
||||
pub prefix: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub show_milliseconds: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
@ -16,9 +15,9 @@ impl<'a> RootModuleConfig<'a> for CmdDurationConfig<'a> {
|
||||
fn new() -> Self {
|
||||
CmdDurationConfig {
|
||||
min_time: 2_000,
|
||||
prefix: "took ",
|
||||
format: "took [$duration]($style) ",
|
||||
show_milliseconds: false,
|
||||
style: Color::Yellow.bold(),
|
||||
style: "yellow bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct CondaConfig<'a> {
|
||||
pub truncation_length: usize,
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub environment: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
@ -16,15 +15,9 @@ impl<'a> RootModuleConfig<'a> for CondaConfig<'a> {
|
||||
fn new() -> Self {
|
||||
CondaConfig {
|
||||
truncation_length: 1,
|
||||
symbol: SegmentConfig {
|
||||
value: "C ",
|
||||
style: None,
|
||||
},
|
||||
environment: SegmentConfig {
|
||||
value: "",
|
||||
style: None,
|
||||
},
|
||||
style: Color::Green.bold(),
|
||||
format: "via [$symbol$environment]($style) ",
|
||||
symbol: "🅒 ",
|
||||
style: "green bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct CrystalConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for CrystalConfig<'a> {
|
||||
fn new() -> Self {
|
||||
CrystalConfig {
|
||||
symbol: SegmentConfig::new("🔮 "),
|
||||
style: Color::Red.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🔮 ",
|
||||
style: "bold red",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig, VecOr};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, VecOr};
|
||||
|
||||
use ansi_term::Style;
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
@ -14,15 +13,14 @@ pub struct Directories<'a>(pub Vec<&'a str>);
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct CustomConfig<'a> {
|
||||
pub symbol: Option<SegmentConfig<'a>>,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub command: &'a str,
|
||||
pub when: Option<&'a str>,
|
||||
pub shell: VecOr<&'a str>,
|
||||
pub description: &'a str,
|
||||
pub style: Option<Style>,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
pub prefix: Option<&'a str>,
|
||||
pub suffix: Option<&'a str>,
|
||||
pub files: Files<'a>,
|
||||
pub extensions: Extensions<'a>,
|
||||
pub directories: Directories<'a>,
|
||||
@ -31,15 +29,14 @@ pub struct CustomConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for CustomConfig<'a> {
|
||||
fn new() -> Self {
|
||||
CustomConfig {
|
||||
symbol: None,
|
||||
format: "[$symbol$output]($style) ",
|
||||
symbol: "",
|
||||
command: "",
|
||||
when: None,
|
||||
shell: VecOr::default(),
|
||||
description: "<custom config>",
|
||||
style: None,
|
||||
style: "green bold",
|
||||
disabled: false,
|
||||
prefix: None,
|
||||
suffix: None,
|
||||
files: Files::default(),
|
||||
extensions: Extensions::default(),
|
||||
directories: Directories::default(),
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
@ -11,8 +10,8 @@ pub struct DirectoryConfig<'a> {
|
||||
pub substitutions: HashMap<String, &'a str>,
|
||||
pub fish_style_pwd_dir_length: i64,
|
||||
pub use_logical_path: bool,
|
||||
pub prefix: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
@ -24,8 +23,8 @@ impl<'a> RootModuleConfig<'a> for DirectoryConfig<'a> {
|
||||
fish_style_pwd_dir_length: 0,
|
||||
substitutions: HashMap::new(),
|
||||
use_logical_path: true,
|
||||
prefix: "in ",
|
||||
style: Color::Cyan.bold(),
|
||||
format: "[$path]($style) ",
|
||||
style: "cyan bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct DockerContextConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub context: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub format: &'a str,
|
||||
pub only_with_files: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
@ -15,9 +14,9 @@ pub struct DockerContextConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for DockerContextConfig<'a> {
|
||||
fn new() -> Self {
|
||||
DockerContextConfig {
|
||||
symbol: SegmentConfig::new("🐳 "),
|
||||
context: SegmentConfig::default(),
|
||||
style: Color::Blue.bold(),
|
||||
symbol: "🐳 ",
|
||||
style: "blue bold",
|
||||
format: "via [$symbol$context]($style) ",
|
||||
only_with_files: true,
|
||||
disabled: false,
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct DotnetConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub heuristic: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
@ -15,9 +14,9 @@ pub struct DotnetConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for DotnetConfig<'a> {
|
||||
fn new() -> Self {
|
||||
DotnetConfig {
|
||||
symbol: SegmentConfig::new("•NET "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Blue.bold(),
|
||||
format: "[$symbol$version( 🎯 $tfm)]($style) ",
|
||||
symbol: "•NET ",
|
||||
style: "blue bold",
|
||||
heuristic: true,
|
||||
disabled: false,
|
||||
}
|
||||
|
@ -1,24 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct ElixirConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub otp_version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for ElixirConfig<'a> {
|
||||
fn new() -> Self {
|
||||
ElixirConfig {
|
||||
symbol: SegmentConfig::new("💧 "),
|
||||
version: SegmentConfig::default(),
|
||||
otp_version: SegmentConfig::default(),
|
||||
style: Color::Purple.bold(),
|
||||
format: "via [$symbol$version \\(OTP $otp_version\\)]($style) ",
|
||||
symbol: "💧 ",
|
||||
style: "bold purple",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct ElmConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for ElmConfig<'a> {
|
||||
fn new() -> Self {
|
||||
ElmConfig {
|
||||
symbol: SegmentConfig::new("🌳 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Cyan.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🌳 ",
|
||||
style: "cyan bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,25 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct EnvVarConfig<'a> {
|
||||
pub symbol: Option<SegmentConfig<'a>>,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub variable: Option<&'a str>,
|
||||
pub default: Option<&'a str>,
|
||||
pub prefix: &'a str,
|
||||
pub suffix: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for EnvVarConfig<'a> {
|
||||
fn new() -> Self {
|
||||
EnvVarConfig {
|
||||
symbol: None,
|
||||
symbol: "",
|
||||
style: "black bold dimmed",
|
||||
variable: None,
|
||||
default: None,
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
style: Color::Black.bold().dimmed(),
|
||||
format: "with [$env_value]($style) ",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct ErlangConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for ErlangConfig<'a> {
|
||||
fn new() -> Self {
|
||||
ErlangConfig {
|
||||
symbol: SegmentConfig::new("🖧 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Red.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🖧 ",
|
||||
style: "bold red",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,25 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct GitBranchConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub truncation_length: i64,
|
||||
pub truncation_symbol: &'a str,
|
||||
pub branch_name: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for GitBranchConfig<'a> {
|
||||
fn new() -> Self {
|
||||
GitBranchConfig {
|
||||
symbol: SegmentConfig::new(" "),
|
||||
format: "on [$symbol$branch]($style) ",
|
||||
symbol: " ",
|
||||
style: "bold purple",
|
||||
truncation_length: std::i64::MAX,
|
||||
truncation_symbol: "…",
|
||||
branch_name: SegmentConfig::default(),
|
||||
style: Color::Purple.bold(),
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct GitCommitConfig<'a> {
|
||||
pub commit_hash_length: usize,
|
||||
pub hash: SegmentConfig<'a>,
|
||||
pub prefix: &'a str,
|
||||
pub suffix: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub only_detached: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
@ -19,10 +16,8 @@ impl<'a> RootModuleConfig<'a> for GitCommitConfig<'a> {
|
||||
GitCommitConfig {
|
||||
// be consistent with git by default, which has DEFAULT_ABBREV set to 7
|
||||
commit_hash_length: 7,
|
||||
hash: SegmentConfig::default(),
|
||||
prefix: "(",
|
||||
suffix: ") ",
|
||||
style: Color::Green.bold(),
|
||||
format: "[\\($hash\\)]($style) ",
|
||||
style: "green bold",
|
||||
only_detached: true,
|
||||
disabled: false,
|
||||
}
|
||||
|
@ -1,34 +1,33 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct GitStateConfig<'a> {
|
||||
pub rebase: SegmentConfig<'a>,
|
||||
pub merge: SegmentConfig<'a>,
|
||||
pub revert: SegmentConfig<'a>,
|
||||
pub cherry_pick: SegmentConfig<'a>,
|
||||
pub bisect: SegmentConfig<'a>,
|
||||
pub am: SegmentConfig<'a>,
|
||||
pub am_or_rebase: SegmentConfig<'a>,
|
||||
pub progress_divider: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub rebase: &'a str,
|
||||
pub merge: &'a str,
|
||||
pub revert: &'a str,
|
||||
pub cherry_pick: &'a str,
|
||||
pub bisect: &'a str,
|
||||
pub am: &'a str,
|
||||
pub am_or_rebase: &'a str,
|
||||
pub style: &'a str,
|
||||
pub format: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for GitStateConfig<'a> {
|
||||
fn new() -> Self {
|
||||
GitStateConfig {
|
||||
rebase: SegmentConfig::new("REBASING"),
|
||||
merge: SegmentConfig::new("MERGING"),
|
||||
revert: SegmentConfig::new("REVERTING"),
|
||||
cherry_pick: SegmentConfig::new("CHERRY-PICKING"),
|
||||
bisect: SegmentConfig::new("BISECTING"),
|
||||
am: SegmentConfig::new("AM"),
|
||||
am_or_rebase: SegmentConfig::new("AM/REBASE"),
|
||||
progress_divider: SegmentConfig::new("/"),
|
||||
style: Color::Yellow.bold(),
|
||||
rebase: "REBASING",
|
||||
merge: "MERGING",
|
||||
revert: "REVERTING",
|
||||
cherry_pick: "CHERRY-PICKING",
|
||||
bisect: "BISECTING",
|
||||
am: "AM",
|
||||
am_or_rebase: "AM/REBASE",
|
||||
style: "bold yellow",
|
||||
format: "[\\($state( $progress_current/$progress_total)\\)]($style) ",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,65 +1,40 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct GitStatusConfig<'a> {
|
||||
pub stashed: SegmentConfig<'a>,
|
||||
pub stashed_count: CountConfig,
|
||||
pub ahead: SegmentConfig<'a>,
|
||||
pub behind: SegmentConfig<'a>,
|
||||
pub diverged: SegmentConfig<'a>,
|
||||
pub show_sync_count: bool,
|
||||
pub conflicted: SegmentConfig<'a>,
|
||||
pub conflicted_count: CountConfig,
|
||||
pub deleted: SegmentConfig<'a>,
|
||||
pub deleted_count: CountConfig,
|
||||
pub renamed: SegmentConfig<'a>,
|
||||
pub renamed_count: CountConfig,
|
||||
pub modified: SegmentConfig<'a>,
|
||||
pub modified_count: CountConfig,
|
||||
pub staged: SegmentConfig<'a>,
|
||||
pub staged_count: CountConfig,
|
||||
pub untracked: SegmentConfig<'a>,
|
||||
pub untracked_count: CountConfig,
|
||||
pub prefix: &'a str,
|
||||
pub suffix: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub stashed: &'a str,
|
||||
pub ahead: &'a str,
|
||||
pub behind: &'a str,
|
||||
pub diverged: &'a str,
|
||||
pub conflicted: &'a str,
|
||||
pub deleted: &'a str,
|
||||
pub renamed: &'a str,
|
||||
pub modified: &'a str,
|
||||
pub staged: &'a str,
|
||||
pub untracked: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for GitStatusConfig<'a> {
|
||||
fn new() -> Self {
|
||||
GitStatusConfig {
|
||||
stashed: SegmentConfig::new("$"),
|
||||
stashed_count: CountConfig::default(),
|
||||
ahead: SegmentConfig::new("⇡"),
|
||||
behind: SegmentConfig::new("⇣"),
|
||||
diverged: SegmentConfig::new("⇕"),
|
||||
conflicted: SegmentConfig::new("="),
|
||||
show_sync_count: false,
|
||||
conflicted_count: CountConfig::default(),
|
||||
deleted: SegmentConfig::new("✘"),
|
||||
deleted_count: CountConfig::default(),
|
||||
renamed: SegmentConfig::new("»"),
|
||||
renamed_count: CountConfig::default(),
|
||||
modified: SegmentConfig::new("!"),
|
||||
modified_count: CountConfig::default(),
|
||||
staged: SegmentConfig::new("+"),
|
||||
staged_count: CountConfig::default(),
|
||||
untracked: SegmentConfig::new("?"),
|
||||
untracked_count: CountConfig::default(),
|
||||
prefix: "[",
|
||||
suffix: "] ",
|
||||
style: Color::Red.bold(),
|
||||
format: "([\\[$all_status$ahead_behind\\]]($style) )",
|
||||
style: "red bold",
|
||||
stashed: "\\$",
|
||||
ahead: "⇡",
|
||||
behind: "⇣",
|
||||
diverged: "⇕",
|
||||
conflicted: "=",
|
||||
deleted: "✘",
|
||||
renamed: "»",
|
||||
modified: "!",
|
||||
staged: "+",
|
||||
untracked: "?",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, ModuleConfig, Default)]
|
||||
pub struct CountConfig {
|
||||
pub enabled: bool,
|
||||
pub style: Option<Style>,
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct GoConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for GoConfig<'a> {
|
||||
fn new() -> Self {
|
||||
GoConfig {
|
||||
symbol: SegmentConfig::new("🐹 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Cyan.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🐹 ",
|
||||
style: "bold cyan",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,25 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct HgBranchConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub format: &'a str,
|
||||
pub truncation_length: i64,
|
||||
pub truncation_symbol: &'a str,
|
||||
pub branch_name: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for HgBranchConfig<'a> {
|
||||
fn new() -> Self {
|
||||
HgBranchConfig {
|
||||
symbol: SegmentConfig::new(" "),
|
||||
symbol: " ",
|
||||
style: "bold purple",
|
||||
format: "on [$symbol$branch]($style) ",
|
||||
truncation_length: std::i64::MAX,
|
||||
truncation_symbol: "…",
|
||||
branch_name: SegmentConfig::default(),
|
||||
style: Color::Purple.bold(),
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct HostnameConfig<'a> {
|
||||
pub ssh_only: bool,
|
||||
pub prefix: &'a str,
|
||||
pub suffix: &'a str,
|
||||
pub trim_at: &'a str,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
@ -17,10 +15,9 @@ impl<'a> RootModuleConfig<'a> for HostnameConfig<'a> {
|
||||
fn new() -> Self {
|
||||
HostnameConfig {
|
||||
ssh_only: true,
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
trim_at: ".",
|
||||
style: Color::Green.bold().dimmed(),
|
||||
format: "on [$hostname]($style) ",
|
||||
style: "green dimmed bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct JavaConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub disabled: bool,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub symbol: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for JavaConfig<'a> {
|
||||
fn new() -> Self {
|
||||
JavaConfig {
|
||||
symbol: SegmentConfig::new("☕ "),
|
||||
style: Color::Red.dimmed(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
disabled: false,
|
||||
style: "red dimmed",
|
||||
symbol: "☕ ",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,23 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct JobsConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub threshold: i64,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for JobsConfig<'a> {
|
||||
fn new() -> Self {
|
||||
JobsConfig {
|
||||
symbol: SegmentConfig::new("✦"),
|
||||
threshold: 1,
|
||||
style: Color::Blue.bold(),
|
||||
format: "[$symbol$number]($style) ",
|
||||
symbol: "✦",
|
||||
style: "bold blue",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct JuliaConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for JuliaConfig<'a> {
|
||||
fn new() -> Self {
|
||||
JuliaConfig {
|
||||
symbol: SegmentConfig::new("ஃ "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Purple.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "ஃ ",
|
||||
style: "bold purple",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct KubernetesConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub context: SegmentConfig<'a>,
|
||||
pub namespace: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub symbol: &'a str,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
pub context_aliases: HashMap<String, &'a str>,
|
||||
}
|
||||
@ -17,10 +15,9 @@ pub struct KubernetesConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for KubernetesConfig<'a> {
|
||||
fn new() -> Self {
|
||||
KubernetesConfig {
|
||||
symbol: SegmentConfig::new("☸ "),
|
||||
context: SegmentConfig::default(),
|
||||
namespace: SegmentConfig::default(),
|
||||
style: Color::Cyan.bold(),
|
||||
symbol: "☸ ",
|
||||
format: "on [$symbol$context( \\($namespace\\))]($style) ",
|
||||
style: "cyan bold",
|
||||
disabled: true,
|
||||
context_aliases: HashMap::new(),
|
||||
}
|
||||
|
@ -1,32 +1,23 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct MemoryConfig<'a> {
|
||||
pub show_percentage: bool,
|
||||
pub show_swap: bool,
|
||||
pub threshold: i64,
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub separator: SegmentConfig<'a>,
|
||||
pub ram: SegmentConfig<'a>,
|
||||
pub swap: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for MemoryConfig<'a> {
|
||||
fn new() -> Self {
|
||||
MemoryConfig {
|
||||
show_percentage: false,
|
||||
show_swap: true,
|
||||
threshold: 75,
|
||||
symbol: SegmentConfig::new("🐏 "),
|
||||
separator: SegmentConfig::new(" | "),
|
||||
ram: SegmentConfig::default(),
|
||||
swap: SegmentConfig::default(),
|
||||
style: Color::White.bold().dimmed(),
|
||||
format: "via $symbol[$ram( | $swap)]($style) ",
|
||||
style: "white bold dimmed",
|
||||
symbol: "🐏 ",
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct NimConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for NimConfig<'a> {
|
||||
fn new() -> Self {
|
||||
NimConfig {
|
||||
symbol: SegmentConfig::new("👑 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Yellow.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "👑 ",
|
||||
style: "yellow bold",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,25 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct NixShellConfig<'a> {
|
||||
pub use_name: bool,
|
||||
pub impure_msg: SegmentConfig<'a>,
|
||||
pub pure_msg: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub impure_msg: &'a str,
|
||||
pub pure_msg: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for NixShellConfig<'a> {
|
||||
fn new() -> Self {
|
||||
NixShellConfig {
|
||||
use_name: false,
|
||||
impure_msg: SegmentConfig::new("impure"),
|
||||
pure_msg: SegmentConfig::new("pure"),
|
||||
style: Color::Blue.bold(),
|
||||
symbol: SegmentConfig::new("❄️ "),
|
||||
format: "via [$symbol$state( \\($name\\))]($style) ",
|
||||
symbol: "❄️ ",
|
||||
style: "bold blue",
|
||||
impure_msg: "impure",
|
||||
pure_msg: "pure",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct NodejsConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for NodejsConfig<'a> {
|
||||
fn new() -> Self {
|
||||
NodejsConfig {
|
||||
symbol: SegmentConfig::new("⬢ "),
|
||||
style: Color::Green.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "⬢ ",
|
||||
style: "bold green",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct OCamlConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for OCamlConfig<'a> {
|
||||
fn new() -> Self {
|
||||
OCamlConfig {
|
||||
symbol: SegmentConfig::new("🐫 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Yellow.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🐫 ",
|
||||
style: "bold yellow",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct PackageConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub display_private: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
@ -14,8 +14,9 @@ pub struct PackageConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for PackageConfig<'a> {
|
||||
fn new() -> Self {
|
||||
PackageConfig {
|
||||
symbol: SegmentConfig::new("📦 "),
|
||||
style: Color::Fixed(208).bold(),
|
||||
format: "is [$symbol$version]($style) ",
|
||||
symbol: "📦 ",
|
||||
style: "208 bold",
|
||||
display_private: false,
|
||||
disabled: false,
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct PhpConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub format: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for PhpConfig<'a> {
|
||||
fn new() -> Self {
|
||||
PhpConfig {
|
||||
symbol: SegmentConfig::new("🐘 "),
|
||||
style: Color::Fixed(147).bold(),
|
||||
symbol: "🐘 ",
|
||||
style: "147 bold",
|
||||
format: "via [$symbol$version]($style) ",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct PureScriptConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for PureScriptConfig<'a> {
|
||||
fn new() -> Self {
|
||||
PureScriptConfig {
|
||||
symbol: SegmentConfig::new("<=> "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::White.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "<=> ",
|
||||
style: "bold white",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,27 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct PythonConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub pyenv_prefix: SegmentConfig<'a>,
|
||||
pub pyenv_version_name: bool,
|
||||
pub python_binary: &'a str,
|
||||
pub scan_for_pyfiles: bool,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for PythonConfig<'a> {
|
||||
fn new() -> Self {
|
||||
PythonConfig {
|
||||
symbol: SegmentConfig::new("🐍 "),
|
||||
version: SegmentConfig::default(),
|
||||
pyenv_prefix: SegmentConfig::new("pyenv "),
|
||||
pyenv_version_name: false,
|
||||
python_binary: "python",
|
||||
scan_for_pyfiles: true,
|
||||
style: Color::Yellow.bold(),
|
||||
format: "via [$symbol$version( \\($virtualenv\\))]($style) ",
|
||||
style: "yellow bold",
|
||||
symbol: "🐍 ",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct RubyConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for RubyConfig<'a> {
|
||||
fn new() -> Self {
|
||||
RubyConfig {
|
||||
symbol: SegmentConfig::new("💎 "),
|
||||
style: Color::Red.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "💎 ",
|
||||
style: "bold red",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct RustConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for RustConfig<'a> {
|
||||
fn new() -> Self {
|
||||
RustConfig {
|
||||
symbol: SegmentConfig::new("🦀 "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Red.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "🦀 ",
|
||||
style: "bold red",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct SingularityConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub label: &'a str,
|
||||
pub prefix: &'a str,
|
||||
pub suffix: &'a str,
|
||||
pub style: Style,
|
||||
pub symbol: &'a str,
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for SingularityConfig<'a> {
|
||||
fn new() -> Self {
|
||||
SingularityConfig {
|
||||
symbol: SegmentConfig::default(),
|
||||
label: "",
|
||||
prefix: "[",
|
||||
suffix: "]",
|
||||
style: Color::Blue.bold().dimmed(),
|
||||
format: "[$symbol\\[$env\\]]($style) ",
|
||||
symbol: "",
|
||||
style: "blue bold dimmed",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -4,66 +4,66 @@ use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct StarshipRootConfig<'a> {
|
||||
pub add_newline: bool,
|
||||
pub prompt_order: Vec<&'a str>,
|
||||
pub format: &'a str,
|
||||
pub scan_timeout: u64,
|
||||
}
|
||||
|
||||
// List of default prompt order
|
||||
// NOTE: If this const value is changed then Default prompt order subheading inside
|
||||
// prompt heading of config docs needs to be updated according to changes made here.
|
||||
pub const PROMPT_ORDER: [&str; 42] = [
|
||||
"username",
|
||||
"hostname",
|
||||
"singularity",
|
||||
"kubernetes",
|
||||
"directory",
|
||||
"git_branch",
|
||||
"git_commit",
|
||||
"git_state",
|
||||
"git_status",
|
||||
"hg_branch",
|
||||
"docker_context",
|
||||
"package",
|
||||
// ↓ Toolchain version modules ↓
|
||||
// (Let's keep these sorted alphabetically)
|
||||
"dotnet",
|
||||
"elixir",
|
||||
"elm",
|
||||
"erlang",
|
||||
"golang",
|
||||
"java",
|
||||
"julia",
|
||||
"nim",
|
||||
"nodejs",
|
||||
"ocaml",
|
||||
"php",
|
||||
"purescript",
|
||||
"python",
|
||||
"ruby",
|
||||
"rust",
|
||||
"terraform",
|
||||
"zig",
|
||||
// ↑ Toolchain version modules ↑
|
||||
"nix_shell",
|
||||
"conda",
|
||||
"memory_usage",
|
||||
"aws",
|
||||
"env_var",
|
||||
"crystal",
|
||||
"cmd_duration",
|
||||
"custom",
|
||||
"line_break",
|
||||
"jobs",
|
||||
#[cfg(feature = "battery")]
|
||||
"battery",
|
||||
"time",
|
||||
"character",
|
||||
];
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> {
|
||||
fn new() -> Self {
|
||||
StarshipRootConfig {
|
||||
add_newline: true,
|
||||
// List of default prompt order
|
||||
// NOTE: If this const value is changed then Default prompt order subheading inside
|
||||
// prompt heading of config docs needs to be updated according to changes made here.
|
||||
prompt_order: vec![
|
||||
"username",
|
||||
"hostname",
|
||||
"singularity",
|
||||
"kubernetes",
|
||||
"directory",
|
||||
"git_branch",
|
||||
"git_commit",
|
||||
"git_state",
|
||||
"git_status",
|
||||
"hg_branch",
|
||||
"docker_context",
|
||||
"package",
|
||||
// ↓ Toolchain version modules ↓
|
||||
// (Let's keep these sorted alphabetically)
|
||||
"dotnet",
|
||||
"elixir",
|
||||
"elm",
|
||||
"erlang",
|
||||
"golang",
|
||||
"java",
|
||||
"julia",
|
||||
"nim",
|
||||
"nodejs",
|
||||
"ocaml",
|
||||
"php",
|
||||
"purescript",
|
||||
"python",
|
||||
"ruby",
|
||||
"rust",
|
||||
"terraform",
|
||||
"zig",
|
||||
// ↑ Toolchain version modules ↑
|
||||
"nix_shell",
|
||||
"conda",
|
||||
"memory_usage",
|
||||
"aws",
|
||||
"env_var",
|
||||
"crystal",
|
||||
"cmd_duration",
|
||||
"custom",
|
||||
"line_break",
|
||||
"jobs",
|
||||
#[cfg(feature = "battery")]
|
||||
"battery",
|
||||
"time",
|
||||
"character",
|
||||
],
|
||||
format: "\n$all",
|
||||
scan_timeout: 30,
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct TerraformConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub workspace: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub show_version: bool,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for TerraformConfig<'a> {
|
||||
fn new() -> Self {
|
||||
TerraformConfig {
|
||||
symbol: SegmentConfig::new("💠 "),
|
||||
workspace: SegmentConfig::default(),
|
||||
version: SegmentConfig::default(),
|
||||
show_version: false,
|
||||
style: Color::Fixed(105).bold(),
|
||||
format: "via [$symbol$workspace]($style) ",
|
||||
symbol: "💠 ",
|
||||
style: "bold 105",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct TimeConfig<'a> {
|
||||
pub format: &'a str,
|
||||
pub style: &'a str,
|
||||
pub use_12hr: bool,
|
||||
pub format: Option<&'a str>,
|
||||
pub style: Style,
|
||||
pub time_format: Option<&'a str>,
|
||||
pub disabled: bool,
|
||||
pub utc_time_offset: &'a str,
|
||||
pub time_range: &'a str,
|
||||
@ -16,9 +16,10 @@ pub struct TimeConfig<'a> {
|
||||
impl<'a> RootModuleConfig<'a> for TimeConfig<'a> {
|
||||
fn new() -> Self {
|
||||
TimeConfig {
|
||||
format: "at [$time]($style) ",
|
||||
style: "bold yellow",
|
||||
use_12hr: false,
|
||||
format: None,
|
||||
style: Color::Yellow.bold(),
|
||||
time_format: None,
|
||||
disabled: true,
|
||||
utc_time_offset: "local",
|
||||
time_range: "-",
|
||||
|
@ -1,21 +1,22 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct UsernameConfig {
|
||||
pub style_root: Style,
|
||||
pub style_user: Style,
|
||||
pub struct UsernameConfig<'a> {
|
||||
pub format: &'a str,
|
||||
pub style_root: &'a str,
|
||||
pub style_user: &'a str,
|
||||
pub show_always: bool,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for UsernameConfig {
|
||||
impl<'a> RootModuleConfig<'a> for UsernameConfig<'a> {
|
||||
fn new() -> Self {
|
||||
UsernameConfig {
|
||||
style_root: Color::Red.bold(),
|
||||
style_user: Color::Yellow.bold(),
|
||||
format: "via [$user]($style) ",
|
||||
style_root: "red bold",
|
||||
style_user: "yellow bold",
|
||||
show_always: false,
|
||||
disabled: false,
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||
use crate::config::{ModuleConfig, RootModuleConfig};
|
||||
|
||||
use ansi_term::{Color, Style};
|
||||
use starship_module_config_derive::ModuleConfig;
|
||||
|
||||
#[derive(Clone, ModuleConfig)]
|
||||
pub struct ZigConfig<'a> {
|
||||
pub symbol: SegmentConfig<'a>,
|
||||
pub version: SegmentConfig<'a>,
|
||||
pub style: Style,
|
||||
pub format: &'a str,
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RootModuleConfig<'a> for ZigConfig<'a> {
|
||||
fn new() -> Self {
|
||||
ZigConfig {
|
||||
symbol: SegmentConfig::new("↯ "),
|
||||
version: SegmentConfig::default(),
|
||||
style: Color::Yellow.bold(),
|
||||
format: "via [$symbol$version]($style) ",
|
||||
symbol: "↯ ",
|
||||
style: "bold yellow",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ pub mod model;
|
||||
mod parser;
|
||||
pub mod string_formatter;
|
||||
|
||||
pub use model::{StyleVariableHolder, VariableHolder};
|
||||
pub use string_formatter::StringFormatter;
|
||||
|
@ -1,17 +1,103 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// Type that holds a number of variables of type `T`
|
||||
pub trait VariableHolder<T> {
|
||||
fn get_variables(&self) -> BTreeSet<T>;
|
||||
}
|
||||
|
||||
/// Type that holds a number of style variables of type `T`
|
||||
pub trait StyleVariableHolder<T> {
|
||||
fn get_style_variables(&self) -> BTreeSet<T>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextGroup<'a> {
|
||||
pub format: Vec<FormatElement<'a>>,
|
||||
pub style: Vec<StyleElement<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FormatElement<'a> {
|
||||
Text(Cow<'a, str>),
|
||||
Variable(Cow<'a, str>),
|
||||
TextGroup(TextGroup<'a>),
|
||||
Conditional(Vec<FormatElement<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum StyleElement<'a> {
|
||||
Text(Cow<'a, str>),
|
||||
Variable(Cow<'a, str>),
|
||||
}
|
||||
|
||||
impl<'a> VariableHolder<Cow<'a, str>> for FormatElement<'a> {
|
||||
fn get_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
match self {
|
||||
FormatElement::Variable(var) => {
|
||||
let mut variables = BTreeSet::new();
|
||||
variables.insert(var.clone());
|
||||
variables
|
||||
}
|
||||
FormatElement::TextGroup(textgroup) => textgroup.format.get_variables(),
|
||||
FormatElement::Conditional(format) => format.get_variables(),
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VariableHolder<Cow<'a, str>> for Vec<FormatElement<'a>> {
|
||||
fn get_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
self.iter().fold(BTreeSet::new(), |mut acc, el| {
|
||||
acc.extend(el.get_variables());
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VariableHolder<Cow<'a, str>> for &[FormatElement<'a>] {
|
||||
fn get_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
self.iter().fold(BTreeSet::new(), |mut acc, el| {
|
||||
acc.extend(el.get_variables());
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StyleVariableHolder<Cow<'a, str>> for StyleElement<'a> {
|
||||
fn get_style_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
match self {
|
||||
StyleElement::Variable(var) => {
|
||||
let mut variables = BTreeSet::new();
|
||||
variables.insert(var.clone());
|
||||
variables
|
||||
}
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StyleVariableHolder<Cow<'a, str>> for Vec<StyleElement<'a>> {
|
||||
fn get_style_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
self.iter().fold(BTreeSet::new(), |mut acc, el| {
|
||||
acc.extend(el.get_style_variables());
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StyleVariableHolder<Cow<'a, str>> for Vec<FormatElement<'a>> {
|
||||
fn get_style_variables(&self) -> BTreeSet<Cow<'a, str>> {
|
||||
self.iter().fold(BTreeSet::new(), |mut acc, el| match el {
|
||||
FormatElement::TextGroup(textgroup) => {
|
||||
acc.extend(textgroup.style.get_style_variables());
|
||||
acc
|
||||
}
|
||||
FormatElement::Conditional(format) => {
|
||||
acc.extend(format.get_style_variables());
|
||||
acc
|
||||
}
|
||||
_ => acc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,18 @@ use super::model::*;
|
||||
#[grammar = "formatter/spec.pest"]
|
||||
struct IdentParser;
|
||||
|
||||
fn _parse_value(value: Pair<Rule>) -> FormatElement {
|
||||
match value.as_rule() {
|
||||
Rule::text => FormatElement::Text(_parse_text(value).into()),
|
||||
Rule::variable => FormatElement::Variable(_parse_variable(value).into()),
|
||||
Rule::textgroup => FormatElement::TextGroup(_parse_textgroup(value)),
|
||||
Rule::conditional => {
|
||||
FormatElement::Conditional(_parse_format(value.into_inner().next().unwrap()))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn _parse_textgroup(textgroup: Pair<Rule>) -> TextGroup {
|
||||
let mut inner_rules = textgroup.into_inner();
|
||||
let format = inner_rules.next().unwrap();
|
||||
@ -22,55 +34,32 @@ fn _parse_variable(variable: Pair<Rule>) -> &str {
|
||||
}
|
||||
|
||||
fn _parse_text(text: Pair<Rule>) -> String {
|
||||
let mut result = String::new();
|
||||
for pair in text.into_inner() {
|
||||
result.push_str(pair.as_str());
|
||||
}
|
||||
result
|
||||
text.into_inner()
|
||||
.map(|pair| pair.as_str().chars())
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn _parse_format(format: Pair<Rule>) -> Vec<FormatElement> {
|
||||
let mut result: Vec<FormatElement> = Vec::new();
|
||||
|
||||
for pair in format.into_inner() {
|
||||
match pair.as_rule() {
|
||||
Rule::text => result.push(FormatElement::Text(_parse_text(pair).into())),
|
||||
Rule::variable => result.push(FormatElement::Variable(_parse_variable(pair).into())),
|
||||
Rule::textgroup => result.push(FormatElement::TextGroup(_parse_textgroup(pair))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
format.into_inner().map(_parse_value).collect()
|
||||
}
|
||||
|
||||
fn _parse_style(style: Pair<Rule>) -> Vec<StyleElement> {
|
||||
let mut result: Vec<StyleElement> = Vec::new();
|
||||
|
||||
for pair in style.into_inner() {
|
||||
match pair.as_rule() {
|
||||
Rule::text => result.push(StyleElement::Text(_parse_text(pair).into())),
|
||||
Rule::variable => result.push(StyleElement::Variable(_parse_variable(pair).into())),
|
||||
style
|
||||
.into_inner()
|
||||
.map(|pair| match pair.as_rule() {
|
||||
Rule::string => StyleElement::Text(pair.as_str().into()),
|
||||
Rule::variable => StyleElement::Variable(_parse_variable(pair).into()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse(format: &str) -> Result<Vec<FormatElement>, Error<Rule>> {
|
||||
let pairs = IdentParser::parse(Rule::expression, format)?;
|
||||
let mut result: Vec<FormatElement> = Vec::new();
|
||||
|
||||
// Lifetime of Segment is the same as result
|
||||
for pair in pairs.take_while(|pair| pair.as_rule() != Rule::EOI) {
|
||||
match pair.as_rule() {
|
||||
Rule::text => result.push(FormatElement::Text(_parse_text(pair).into())),
|
||||
Rule::variable => result.push(FormatElement::Variable(_parse_variable(pair).into())),
|
||||
Rule::textgroup => result.push(FormatElement::TextGroup(_parse_textgroup(pair))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
IdentParser::parse(Rule::expression, format).map(|pairs| {
|
||||
pairs
|
||||
.take_while(|pair| pair.as_rule() != Rule::EOI)
|
||||
.map(_parse_value)
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
@ -1,16 +1,51 @@
|
||||
// Expression
|
||||
//
|
||||
// The expression of the format string.
|
||||
//
|
||||
// Should be started with SOI and ended with EOI, with a format string in it.
|
||||
expression = _{ SOI ~ value* ~ EOI }
|
||||
value = _{ text | variable | textgroup }
|
||||
value = _{ text | variable | textgroup | conditional }
|
||||
|
||||
variable = { "$" ~ variable_name }
|
||||
variable_name = @{ char+ }
|
||||
// Variable
|
||||
//
|
||||
// A variable is defined as one of the following:
|
||||
//
|
||||
// - A valid variable name followed by a `$` character (`$[a-zA-Z_][a-zA-Z0-9_]*`),
|
||||
// e.g. `$variable`.
|
||||
//
|
||||
// - Some texts wrapped in a curly bracket (`${[^\(\)\[\]\\\${}]+}`),
|
||||
// e.g. `${env:HOST}`.
|
||||
variable = { "$" ~ (variable_name | variable_scope) }
|
||||
variable_name = @{ ('a'..'z' | 'A'..'Z' | "_") ~ char* }
|
||||
char = _{ 'a'..'z' | 'A'..'Z' | '0'..'9' | "_" }
|
||||
|
||||
text = { text_inner+ }
|
||||
text_inner = _{ text_inner_char | escape }
|
||||
text_inner_char = { !("[" | "]" | "(" | ")" | "$" | "\\") ~ ANY }
|
||||
variable_scope = _{ "{" ~ variable_scoped_name ~ "}" }
|
||||
variable_scoped_name = { scoped_char+ }
|
||||
scoped_char = _{ !(escaped_char | "{" | "}") ~ ANY }
|
||||
|
||||
// Text
|
||||
//
|
||||
// Texts can be one of `string` or `escaped_char`, where string is one or more of
|
||||
// unescapable chars.
|
||||
//
|
||||
// This is implemented so as to ensure all functional characters are escaped.
|
||||
text = { (string | escape)+ }
|
||||
string = @{ text_inner_char+ }
|
||||
text_inner_char = { !escaped_char ~ ANY }
|
||||
escape = _{ "\\" ~ escaped_char }
|
||||
escaped_char = { "[" | "]" | "(" | ")" | "\\" | "$" }
|
||||
|
||||
// TextGroup
|
||||
//
|
||||
// A textgroup is a pair of `format` and `style` (`[format](style)`)
|
||||
//
|
||||
// - `format`: A format string, can contain any number of variables, texts or textgroups.
|
||||
// - `style`: A style string, can contain any number of variables or texts.
|
||||
textgroup = { "[" ~ format ~ "]" ~ "(" ~ style ~ ")" }
|
||||
format = { (variable | text | textgroup)* }
|
||||
style = { (variable | text)* }
|
||||
format = { value* }
|
||||
style = { (variable | string)* }
|
||||
|
||||
// Conditional
|
||||
//
|
||||
// A conditional format string that won't render if all the containing variables are empty.
|
||||
conditional = { "(" ~ format ~ ")" }
|
||||
|
@ -1,7 +1,11 @@
|
||||
use ansi_term::Style;
|
||||
use pest::error::Error;
|
||||
use pest::error::Error as PestError;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use crate::config::parse_style_string;
|
||||
use crate::segment::Segment;
|
||||
@ -10,109 +14,263 @@ use super::model::*;
|
||||
use super::parser::{parse, Rule};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum VariableValue {
|
||||
Plain(String),
|
||||
enum VariableValue<'a> {
|
||||
Plain(Cow<'a, str>),
|
||||
Styled(Vec<Segment>),
|
||||
Meta(Vec<FormatElement<'a>>),
|
||||
}
|
||||
|
||||
impl Default for VariableValue {
|
||||
impl<'a> Default for VariableValue<'a> {
|
||||
fn default() -> Self {
|
||||
VariableValue::Plain(String::new())
|
||||
VariableValue::Plain(Cow::Borrowed(""))
|
||||
}
|
||||
}
|
||||
|
||||
type VariableMapType = BTreeMap<String, Option<VariableValue>>;
|
||||
type VariableMapType<'a> =
|
||||
BTreeMap<String, Option<Result<VariableValue<'a>, StringFormatterError>>>;
|
||||
type StyleVariableMapType<'a> =
|
||||
BTreeMap<String, Option<Result<Cow<'a, str>, StringFormatterError>>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StringFormatterError {
|
||||
Custom(String),
|
||||
Parse(PestError<Rule>),
|
||||
}
|
||||
|
||||
impl fmt::Display for StringFormatterError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Custom(error) => write!(f, "{}", error),
|
||||
Self::Parse(error) => write!(f, "{}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for StringFormatterError {}
|
||||
|
||||
impl From<String> for StringFormatterError {
|
||||
fn from(error: String) -> Self {
|
||||
StringFormatterError::Custom(error)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StringFormatter<'a> {
|
||||
format: Vec<FormatElement<'a>>,
|
||||
variables: VariableMapType,
|
||||
variables: VariableMapType<'a>,
|
||||
style_variables: StyleVariableMapType<'a>,
|
||||
}
|
||||
|
||||
impl<'a> StringFormatter<'a> {
|
||||
/// Creates an instance of StringFormatter from a format string
|
||||
pub fn new(format: &'a str) -> Result<Self, Error<Rule>> {
|
||||
///
|
||||
/// This method will throw an Error when the given format string fails to parse.
|
||||
pub fn new(format: &'a str) -> Result<Self, StringFormatterError> {
|
||||
parse(format)
|
||||
.map(|format| {
|
||||
let variables = _get_variables(&format);
|
||||
(format, variables)
|
||||
// Cache all variables
|
||||
let variables = VariableMapType::from_iter(
|
||||
format
|
||||
.get_variables()
|
||||
.into_iter()
|
||||
.map(|key| (key.to_string(), None))
|
||||
.collect::<Vec<(String, Option<_>)>>(),
|
||||
);
|
||||
let style_variables = StyleVariableMapType::from_iter(
|
||||
format
|
||||
.get_style_variables()
|
||||
.into_iter()
|
||||
.map(|key| (key.to_string(), None))
|
||||
.collect::<Vec<(String, Option<_>)>>(),
|
||||
);
|
||||
(format, variables, style_variables)
|
||||
})
|
||||
.map(|(format, variables)| Self { format, variables })
|
||||
.map(|(format, variables, style_variables)| Self {
|
||||
format,
|
||||
variables,
|
||||
style_variables,
|
||||
})
|
||||
.map_err(StringFormatterError::Parse)
|
||||
}
|
||||
|
||||
/// Maps variable name to its value
|
||||
pub fn map(mut self, mapper: impl Fn(&str) -> Option<String> + Sync) -> Self {
|
||||
self.variables.par_iter_mut().for_each(|(key, value)| {
|
||||
*value = mapper(key).map(VariableValue::Plain);
|
||||
});
|
||||
///
|
||||
/// You should provide a function or closure that accepts the variable name `name: &str` as a
|
||||
/// parameter and returns the one of the following values:
|
||||
///
|
||||
/// - `None`: This variable will be reserved for further mappers. If it is `None` when
|
||||
/// `self.parse()` is called, it will be dropped.
|
||||
///
|
||||
/// - `Some(Err(StringFormatterError))`: This variable will throws `StringFormatterError` when
|
||||
/// `self.parse()` is called. Return this if some fatal error occurred and the format string
|
||||
/// should not be rendered.
|
||||
///
|
||||
/// - `Some(Ok(_))`: The value of this variable will be displayed in the format string.
|
||||
///
|
||||
pub fn map<T, M>(mut self, mapper: M) -> Self
|
||||
where
|
||||
T: Into<Cow<'a, str>>,
|
||||
M: Fn(&str) -> Option<Result<T, StringFormatterError>> + Sync,
|
||||
{
|
||||
self.variables
|
||||
.par_iter_mut()
|
||||
.filter(|(_, value)| value.is_none())
|
||||
.for_each(|(key, value)| {
|
||||
*value = mapper(key).map(|var| var.map(|var| VariableValue::Plain(var.into())));
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Maps a meta-variable to a format string containing other variables.
|
||||
///
|
||||
/// This function should be called **before** other map methods so that variables found in
|
||||
/// the format strings of meta-variables can be cached properly.
|
||||
///
|
||||
/// See `StringFormatter::map` for description on the parameters.
|
||||
pub fn map_meta<M>(mut self, mapper: M) -> Self
|
||||
where
|
||||
M: Fn(&str, &BTreeSet<String>) -> Option<&'a str> + Sync,
|
||||
{
|
||||
let variables = self.get_variables();
|
||||
let (variables, style_variables) = self
|
||||
.variables
|
||||
.iter_mut()
|
||||
.filter(|(_, value)| value.is_none())
|
||||
.fold(
|
||||
(VariableMapType::new(), StyleVariableMapType::new()),
|
||||
|(mut v, mut sv), (key, value)| {
|
||||
*value = mapper(key, &variables).map(|format| {
|
||||
StringFormatter::new(format).map(|formatter| {
|
||||
let StringFormatter {
|
||||
format,
|
||||
mut variables,
|
||||
mut style_variables,
|
||||
} = formatter;
|
||||
|
||||
// Add variables in meta variables to self
|
||||
v.append(&mut variables);
|
||||
sv.append(&mut style_variables);
|
||||
|
||||
VariableValue::Meta(format)
|
||||
})
|
||||
});
|
||||
|
||||
(v, sv)
|
||||
},
|
||||
);
|
||||
|
||||
self.variables.extend(variables);
|
||||
self.style_variables.extend(style_variables);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Maps variable name to an array of segments
|
||||
pub fn map_variables_to_segments(
|
||||
mut self,
|
||||
mapper: impl Fn(&str) -> Option<Vec<Segment>> + Sync,
|
||||
) -> Self {
|
||||
self.variables.par_iter_mut().for_each(|(key, value)| {
|
||||
*value = mapper(key).map(VariableValue::Styled);
|
||||
});
|
||||
///
|
||||
/// See `StringFormatter::map` for description on the parameters.
|
||||
pub fn map_variables_to_segments<M>(mut self, mapper: M) -> Self
|
||||
where
|
||||
M: Fn(&str) -> Option<Result<Vec<Segment>, StringFormatterError>> + Sync,
|
||||
{
|
||||
self.variables
|
||||
.par_iter_mut()
|
||||
.filter(|(_, value)| value.is_none())
|
||||
.for_each(|(key, value)| {
|
||||
*value = mapper(key).map(|var| var.map(VariableValue::Styled));
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Maps variable name in a style string to its value
|
||||
///
|
||||
/// See `StringFormatter::map` for description on the parameters.
|
||||
pub fn map_style<T, M>(mut self, mapper: M) -> Self
|
||||
where
|
||||
T: Into<Cow<'a, str>>,
|
||||
M: Fn(&str) -> Option<Result<T, StringFormatterError>> + Sync,
|
||||
{
|
||||
self.style_variables
|
||||
.par_iter_mut()
|
||||
.filter(|(_, value)| value.is_none())
|
||||
.for_each(|(key, value)| {
|
||||
*value = mapper(key).map(|var| var.map(|var| var.into()));
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Parse the format string and consume self.
|
||||
pub fn parse(self, default_style: Option<Style>) -> Vec<Segment> {
|
||||
///
|
||||
/// This method will throw an Error in the following conditions:
|
||||
///
|
||||
/// - Format string in meta variables fails to parse
|
||||
/// - Variable mapper returns an error.
|
||||
pub fn parse(self, default_style: Option<Style>) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
fn _parse_textgroup<'a>(
|
||||
textgroup: TextGroup<'a>,
|
||||
variables: &'a VariableMapType,
|
||||
) -> Vec<Segment> {
|
||||
let style = _parse_style(textgroup.style);
|
||||
_parse_format(textgroup.format, style, &variables)
|
||||
variables: &'a VariableMapType<'a>,
|
||||
style_variables: &'a StyleVariableMapType<'a>,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
let style = _parse_style(textgroup.style, style_variables);
|
||||
_parse_format(
|
||||
textgroup.format,
|
||||
style.transpose()?,
|
||||
&variables,
|
||||
&style_variables,
|
||||
)
|
||||
}
|
||||
|
||||
fn _parse_style(style: Vec<StyleElement>) -> Option<Style> {
|
||||
let style_string = style
|
||||
.iter()
|
||||
.flat_map(|style| match style {
|
||||
StyleElement::Text(text) => text.as_ref().chars(),
|
||||
StyleElement::Variable(variable) => {
|
||||
log::warn!(
|
||||
"Variable `{}` monitored in style string, which is not allowed",
|
||||
&variable
|
||||
);
|
||||
"".chars()
|
||||
fn _parse_style<'a>(
|
||||
style: Vec<StyleElement>,
|
||||
variables: &'a StyleVariableMapType<'a>,
|
||||
) -> Option<Result<Style, StringFormatterError>> {
|
||||
let style_strings = style
|
||||
.into_iter()
|
||||
.map(|style| match style {
|
||||
StyleElement::Text(text) => Ok(text),
|
||||
StyleElement::Variable(name) => {
|
||||
let variable = variables.get(name.as_ref()).unwrap_or(&None);
|
||||
match variable {
|
||||
Some(style_string) => style_string.clone().map(|string| string),
|
||||
None => Ok("".into()),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
parse_style_string(&style_string)
|
||||
.collect::<Result<Vec<Cow<str>>, StringFormatterError>>();
|
||||
style_strings
|
||||
.map(|style_strings| {
|
||||
let style_string: String =
|
||||
style_strings.iter().flat_map(|s| s.chars()).collect();
|
||||
parse_style_string(&style_string)
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
fn _parse_format<'a>(
|
||||
mut format: Vec<FormatElement<'a>>,
|
||||
format: Vec<FormatElement<'a>>,
|
||||
style: Option<Style>,
|
||||
variables: &'a VariableMapType,
|
||||
) -> Vec<Segment> {
|
||||
let mut result: Vec<Segment> = Vec::new();
|
||||
|
||||
format.reverse();
|
||||
while let Some(el) = format.pop() {
|
||||
let mut segments = match el {
|
||||
FormatElement::Text(text) => {
|
||||
vec![_new_segment("_text".into(), text.into_owned(), style)]
|
||||
}
|
||||
FormatElement::TextGroup(textgroup) => {
|
||||
let textgroup = TextGroup {
|
||||
format: textgroup.format,
|
||||
style: textgroup.style,
|
||||
};
|
||||
_parse_textgroup(textgroup, &variables)
|
||||
}
|
||||
FormatElement::Variable(name) => variables
|
||||
.get(name.as_ref())
|
||||
.map(|segments| {
|
||||
let value = segments.clone().unwrap_or_default();
|
||||
match value {
|
||||
VariableValue::Styled(segments) => segments
|
||||
variables: &'a VariableMapType<'a>,
|
||||
style_variables: &'a StyleVariableMapType<'a>,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
let results: Result<Vec<Vec<Segment>>, StringFormatterError> = format
|
||||
.into_iter()
|
||||
.map(|el| {
|
||||
match el {
|
||||
FormatElement::Text(text) => Ok(vec![_new_segment("_text", text, style)]),
|
||||
FormatElement::TextGroup(textgroup) => {
|
||||
let textgroup = TextGroup {
|
||||
format: textgroup.format,
|
||||
style: textgroup.style,
|
||||
};
|
||||
_parse_textgroup(textgroup, &variables, &style_variables)
|
||||
}
|
||||
FormatElement::Variable(name) => variables
|
||||
.get(name.as_ref())
|
||||
.expect("Uncached variable found")
|
||||
.as_ref()
|
||||
.map(|segments| match segments.clone()? {
|
||||
VariableValue::Styled(segments) => Ok(segments
|
||||
.into_iter()
|
||||
.map(|mut segment| {
|
||||
// Derive upper style if the style of segments are none.
|
||||
if !segment.has_style() {
|
||||
if let Some(style) = style {
|
||||
segment.set_style(style);
|
||||
@ -120,80 +278,124 @@ impl<'a> StringFormatter<'a> {
|
||||
}
|
||||
segment
|
||||
})
|
||||
.collect(),
|
||||
.collect()),
|
||||
VariableValue::Plain(text) => {
|
||||
vec![_new_segment(name.to_string(), text, style)]
|
||||
Ok(vec![_new_segment(name, text, style)])
|
||||
}
|
||||
VariableValue::Meta(format) => {
|
||||
let formatter = StringFormatter {
|
||||
format,
|
||||
variables: _clone_without_meta(variables),
|
||||
style_variables: style_variables.clone(),
|
||||
};
|
||||
formatter.parse(style)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| Ok(Vec::new())),
|
||||
FormatElement::Conditional(format) => {
|
||||
// Show the conditional format string if all the variables inside are not
|
||||
// none.
|
||||
fn _should_show_elements<'a>(
|
||||
format_elements: &[FormatElement],
|
||||
variables: &'a VariableMapType<'a>,
|
||||
) -> bool {
|
||||
format_elements.get_variables().iter().any(|var| {
|
||||
variables
|
||||
.get(var.as_ref())
|
||||
.map(|map_result| {
|
||||
let map_result = map_result.as_ref();
|
||||
map_result
|
||||
.and_then(|result| result.as_ref().ok())
|
||||
.map(|result| match result {
|
||||
// If the variable is a meta variable, also
|
||||
// check the format string inside it.
|
||||
VariableValue::Meta(meta_elements) => {
|
||||
let meta_variables =
|
||||
_clone_without_meta(variables);
|
||||
_should_show_elements(
|
||||
&meta_elements,
|
||||
&meta_variables,
|
||||
)
|
||||
}
|
||||
_ => true,
|
||||
})
|
||||
// The variable is None or Err, or a meta variable
|
||||
// that shouldn't show
|
||||
.unwrap_or(false)
|
||||
})
|
||||
// Can't find the variable in format string
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
result.append(&mut segments);
|
||||
}
|
||||
|
||||
result
|
||||
let should_show: bool = _should_show_elements(&format, variables);
|
||||
|
||||
if should_show {
|
||||
_parse_format(format, style, variables, style_variables)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(results?.into_iter().flatten().collect())
|
||||
}
|
||||
|
||||
_parse_format(self.format, default_style, &self.variables)
|
||||
_parse_format(
|
||||
self.format,
|
||||
default_style,
|
||||
&self.variables,
|
||||
&self.style_variables,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract variable names from an array of `FormatElement` into a `BTreeMap`
|
||||
fn _get_variables<'a>(format: &[FormatElement<'a>]) -> VariableMapType {
|
||||
let mut variables: VariableMapType = Default::default();
|
||||
|
||||
fn _push_variables_from_textgroup<'a>(
|
||||
variables: &mut VariableMapType,
|
||||
textgroup: &'a TextGroup<'a>,
|
||||
) {
|
||||
for el in &textgroup.format {
|
||||
match el {
|
||||
FormatElement::Variable(name) => _push_variable(variables, name.as_ref()),
|
||||
FormatElement::TextGroup(textgroup) => {
|
||||
_push_variables_from_textgroup(variables, &textgroup)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for el in &textgroup.style {
|
||||
if let StyleElement::Variable(name) = el {
|
||||
_push_variable(variables, name.as_ref())
|
||||
}
|
||||
}
|
||||
impl<'a> VariableHolder<String> for StringFormatter<'a> {
|
||||
fn get_variables(&self) -> BTreeSet<String> {
|
||||
BTreeSet::from_iter(self.variables.keys().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
fn _push_variable<'a>(variables: &mut VariableMapType, name: &'a str) {
|
||||
variables.insert(name.to_owned(), None);
|
||||
impl<'a> StyleVariableHolder<String> for StringFormatter<'a> {
|
||||
fn get_style_variables(&self) -> BTreeSet<String> {
|
||||
BTreeSet::from_iter(self.style_variables.keys().cloned())
|
||||
}
|
||||
|
||||
for el in format {
|
||||
match el {
|
||||
FormatElement::Variable(name) => _push_variable(&mut variables, name.as_ref()),
|
||||
FormatElement::TextGroup(textgroup) => {
|
||||
_push_variables_from_textgroup(&mut variables, &textgroup)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
variables
|
||||
}
|
||||
|
||||
/// Helper function to create a new segment
|
||||
fn _new_segment(name: String, value: String, style: Option<Style>) -> Segment {
|
||||
fn _new_segment(
|
||||
name: impl Into<String>,
|
||||
value: impl Into<String>,
|
||||
style: Option<Style>,
|
||||
) -> Segment {
|
||||
Segment {
|
||||
_name: name,
|
||||
value,
|
||||
_name: name.into(),
|
||||
value: value.into(),
|
||||
style,
|
||||
}
|
||||
}
|
||||
|
||||
fn _clone_without_meta<'a>(variables: &VariableMapType<'a>) -> VariableMapType<'a> {
|
||||
VariableMapType::from_iter(variables.iter().map(|(key, value)| {
|
||||
let value = match value {
|
||||
Some(Ok(value)) => match value {
|
||||
VariableValue::Meta(_) => None,
|
||||
other => Some(Ok(other.clone())),
|
||||
},
|
||||
Some(Err(e)) => Some(Err(e.clone())),
|
||||
None => None,
|
||||
};
|
||||
(key.clone(), value)
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ansi_term::Color;
|
||||
|
||||
// match_next(result: Iter<Segment>, value, style)
|
||||
// match_next(result: IterMut<Segment>, value, style)
|
||||
macro_rules! match_next {
|
||||
($iter:ident, $value:literal, $($style:tt)+) => {
|
||||
let _next = $iter.next().unwrap();
|
||||
@ -202,7 +404,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_mapper(_: &str) -> Option<String> {
|
||||
fn empty_mapper(_: &str) -> Option<Result<String, StringFormatterError>> {
|
||||
None
|
||||
}
|
||||
|
||||
@ -212,7 +414,7 @@ mod tests {
|
||||
let style = Some(Color::Red.bold());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(style);
|
||||
let result = formatter.parse(style).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", style);
|
||||
}
|
||||
@ -221,7 +423,7 @@ mod tests {
|
||||
fn test_textgroup_text_only() {
|
||||
const FORMAT_STR: &str = "[text](red bold)";
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(None);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", Some(Color::Red.bold()));
|
||||
}
|
||||
@ -233,20 +435,48 @@ mod tests {
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|variable| match variable {
|
||||
"var1" => Some("text1".to_owned()),
|
||||
"var1" => Some(Ok("text1".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text1", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_in_style() {
|
||||
const FORMAT_STR: &str = "[root]($style)";
|
||||
let root_style = Some(Color::Red.bold());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok("red bold".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "root", root_style);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scoped_variable() {
|
||||
const FORMAT_STR: &str = "${env:PWD}";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|variable| Some(Ok(format!("${{{}}}", variable))));
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "${env:PWD}", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escaped_chars() {
|
||||
const FORMAT_STR: &str = r#"\\\[\$text\]\(red bold\)"#;
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(None);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, r#"\[$text](red bold)"#, None);
|
||||
}
|
||||
@ -259,7 +489,7 @@ mod tests {
|
||||
let inner_style = Some(Color::Blue.normal());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(outer_style);
|
||||
let result = formatter.parse(outer_style).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "outer ", outer_style);
|
||||
match_next!(result_iter, "middle ", middle_style);
|
||||
@ -274,10 +504,10 @@ mod tests {
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|variable| match variable {
|
||||
"var" => Some("text".to_owned()),
|
||||
"var" => Some(Ok("text".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", var_style);
|
||||
}
|
||||
@ -292,7 +522,7 @@ mod tests {
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map_variables_to_segments(|variable| match variable {
|
||||
"var" => Some(vec![
|
||||
"var" => Some(Ok(vec![
|
||||
_new_segment("_1".to_owned(), "styless".to_owned(), None),
|
||||
_new_segment("_2".to_owned(), "styled".to_owned(), styled_style),
|
||||
_new_segment(
|
||||
@ -300,16 +530,133 @@ mod tests {
|
||||
"styled_no_modifier".to_owned(),
|
||||
styled_no_modifier_style,
|
||||
),
|
||||
]),
|
||||
])),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "styless", var_style);
|
||||
match_next!(result_iter, "styled", styled_style);
|
||||
match_next!(result_iter, "styled_no_modifier", styled_no_modifier_style);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_meta_variable() {
|
||||
const FORMAT_STR: &str = "$all";
|
||||
const FORMAT_STR__ALL: &str = "$a$b";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map_meta(|var, _| match var {
|
||||
"all" => Some(FORMAT_STR__ALL),
|
||||
_ => None,
|
||||
})
|
||||
.map(|var| match var {
|
||||
"a" => Some(Ok("$a")),
|
||||
"b" => Some(Ok("$b")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$a", None);
|
||||
match_next!(result_iter, "$b", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_mapper() {
|
||||
const FORMAT_STR: &str = "$a$b$c";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|var| match var {
|
||||
"a" => Some(Ok("$a")),
|
||||
"b" => Some(Ok("$b")),
|
||||
_ => None,
|
||||
})
|
||||
.map(|var| match var {
|
||||
"b" => Some(Ok("$B")),
|
||||
"c" => Some(Ok("$c")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$a", None);
|
||||
match_next!(result_iter, "$b", None);
|
||||
match_next!(result_iter, "$c", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional() {
|
||||
const FORMAT_STR: &str = "($some) should render but ($none) shouldn't";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|var| match var {
|
||||
"some" => Some(Ok("$some")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$some", None);
|
||||
match_next!(result_iter, " should render but ", None);
|
||||
match_next!(result_iter, " shouldn't", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_conditional() {
|
||||
const FORMAT_STR: &str = "($some ($none)) and ($none ($some))";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|var| match var {
|
||||
"some" => Some(Ok("$some")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$some", None);
|
||||
match_next!(result_iter, " ", None);
|
||||
match_next!(result_iter, " and ", None);
|
||||
match_next!(result_iter, " ", None);
|
||||
match_next!(result_iter, "$some", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_meta_variable() {
|
||||
const FORMAT_STR: &str = r"(\[$all\]) ";
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map_meta(|var, _| match var {
|
||||
"all" => Some("$some"),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, " ", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_holder() {
|
||||
const FORMAT_STR: &str = "($a [($b) $c](none $s)) $d [t]($t)";
|
||||
let expected_variables =
|
||||
BTreeSet::from_iter(vec!["a", "b", "c", "d"].into_iter().map(String::from));
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let variables = formatter.get_variables();
|
||||
assert_eq!(variables, expected_variables);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_variable_holder() {
|
||||
const FORMAT_STR: &str = "($a [($b) $c](none $s)) $d [t]($t)";
|
||||
let expected_variables = BTreeSet::from_iter(vec!["s", "t"].into_iter().map(String::from));
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let variables = formatter.get_style_variables();
|
||||
assert_eq!(variables, expected_variables);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_error() {
|
||||
// brackets without escape
|
||||
@ -323,4 +670,21 @@ mod tests {
|
||||
assert!(StringFormatter::new(FORMAT_STR).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_error() {
|
||||
const FORMAT_STR: &str = "$never$some";
|
||||
let never_error = StringFormatterError::Custom("NEVER".to_owned());
|
||||
|
||||
let segments = StringFormatter::new(FORMAT_STR).and_then(|formatter| {
|
||||
formatter
|
||||
.map(|var| match var {
|
||||
"some" => Some(Ok("some")),
|
||||
"never" => Some(Err(never_error.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
assert!(segments.is_err());
|
||||
}
|
||||
}
|
||||
|
130
src/module.rs
130
src/module.rs
@ -1,8 +1,6 @@
|
||||
use crate::config::SegmentConfig;
|
||||
use crate::context::Shell;
|
||||
use crate::segment::Segment;
|
||||
use crate::utils::wrap_colorseq_for_shell;
|
||||
use ansi_term::Style;
|
||||
use ansi_term::{ANSIString, ANSIStrings};
|
||||
use std::fmt;
|
||||
|
||||
@ -66,17 +64,8 @@ pub struct Module<'a> {
|
||||
/// The module's description
|
||||
description: String,
|
||||
|
||||
/// The styling to be inherited by all segments contained within this module.
|
||||
style: Style,
|
||||
|
||||
/// The prefix used to separate the current module from the previous one.
|
||||
prefix: Affix,
|
||||
|
||||
/// The collection of segments that compose this module.
|
||||
segments: Vec<Segment>,
|
||||
|
||||
/// The suffix used to separate the current module from the next one.
|
||||
suffix: Affix,
|
||||
pub segments: Vec<Segment>,
|
||||
}
|
||||
|
||||
impl<'a> Module<'a> {
|
||||
@ -86,23 +75,10 @@ impl<'a> Module<'a> {
|
||||
config,
|
||||
_name: name.to_string(),
|
||||
description: desc.to_string(),
|
||||
style: Style::default(),
|
||||
prefix: Affix::default_prefix(name),
|
||||
segments: Vec::new(),
|
||||
suffix: Affix::default_suffix(name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to a newly created segment in the module
|
||||
pub fn create_segment(&mut self, name: &str, segment_config: &SegmentConfig) -> &mut Segment {
|
||||
let mut segment = Segment::new(name);
|
||||
segment.set_style(segment_config.style.unwrap_or(self.style));
|
||||
segment.set_value(segment_config.value);
|
||||
self.segments.push(segment);
|
||||
|
||||
self.segments.last_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Set segments in module
|
||||
pub fn set_segments(&mut self, segments: Vec<Segment>) {
|
||||
self.segments = segments;
|
||||
@ -123,31 +99,11 @@ impl<'a> Module<'a> {
|
||||
self.segments.iter().all(|segment| segment.is_empty())
|
||||
}
|
||||
|
||||
/// Get values of the module's segments
|
||||
pub fn get_segments(&self) -> Vec<&str> {
|
||||
self.segments.iter().map(Segment::get_value).collect()
|
||||
}
|
||||
|
||||
/// Get the module's prefix
|
||||
pub fn get_prefix(&mut self) -> &mut Affix {
|
||||
&mut self.prefix
|
||||
}
|
||||
|
||||
/// Get the module's suffix
|
||||
pub fn get_suffix(&mut self) -> &mut Affix {
|
||||
&mut self.suffix
|
||||
}
|
||||
|
||||
/// Sets the style of the segment.
|
||||
///
|
||||
/// Accepts either `Color` or `Style`.
|
||||
pub fn set_style<T>(&mut self, style: T) -> &mut Module<'a>
|
||||
where
|
||||
T: Into<Style>,
|
||||
{
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a vector of colored ANSIString elements to be later used with
|
||||
/// `ANSIStrings()` to optimize ANSI codes
|
||||
pub fn ansi_strings(&self) -> Vec<ANSIString> {
|
||||
@ -155,26 +111,17 @@ impl<'a> Module<'a> {
|
||||
}
|
||||
|
||||
pub fn ansi_strings_for_shell(&self, shell: Shell) -> Vec<ANSIString> {
|
||||
let mut ansi_strings = self
|
||||
let ansi_strings = self
|
||||
.segments
|
||||
.iter()
|
||||
.map(Segment::ansi_string)
|
||||
.collect::<Vec<ANSIString>>();
|
||||
|
||||
ansi_strings.insert(0, self.prefix.ansi_string());
|
||||
ansi_strings.push(self.suffix.ansi_string());
|
||||
|
||||
ansi_strings = match shell {
|
||||
match shell {
|
||||
Shell::Bash => ansi_strings_modified(ansi_strings, shell),
|
||||
Shell::Zsh => ansi_strings_modified(ansi_strings, shell),
|
||||
_ => ansi_strings,
|
||||
};
|
||||
|
||||
ansi_strings
|
||||
}
|
||||
|
||||
pub fn to_string_without_prefix(&self, shell: Shell) -> String {
|
||||
ANSIStrings(&self.ansi_strings_for_shell(shell)[1..]).to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,67 +142,6 @@ fn ansi_strings_modified(ansi_strings: Vec<ANSIString>, shell: Shell) -> Vec<ANS
|
||||
.collect::<Vec<ANSIString>>()
|
||||
}
|
||||
|
||||
/// Module affixes are to be used for the prefix or suffix of a module.
|
||||
pub struct Affix {
|
||||
/// The affix's name, to be used in configuration and logging.
|
||||
_name: String,
|
||||
|
||||
/// The affix's style.
|
||||
style: Style,
|
||||
|
||||
/// The string value of the affix.
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Affix {
|
||||
pub fn default_prefix(name: &str) -> Self {
|
||||
Self {
|
||||
_name: format!("{}_prefix", name),
|
||||
style: Style::default(),
|
||||
value: "via ".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_suffix(name: &str) -> Self {
|
||||
Self {
|
||||
_name: format!("{}_suffix", name),
|
||||
style: Style::default(),
|
||||
value: " ".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the style of the module.
|
||||
///
|
||||
/// Accepts either `Color` or `Style`.
|
||||
pub fn set_style<T>(&mut self, style: T) -> &mut Self
|
||||
where
|
||||
T: Into<Style>,
|
||||
{
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the value of the module.
|
||||
pub fn set_value<T>(&mut self, value: T) -> &mut Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.value = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Generates the colored ANSIString output.
|
||||
pub fn ansi_string(&self) -> ANSIString {
|
||||
self.style.paint(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Affix {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.ansi_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -268,10 +154,7 @@ mod tests {
|
||||
config: None,
|
||||
_name: name.to_string(),
|
||||
description: desc.to_string(),
|
||||
style: Style::default(),
|
||||
prefix: Affix::default_prefix(name),
|
||||
segments: Vec::new(),
|
||||
suffix: Affix::default_suffix(name),
|
||||
};
|
||||
|
||||
assert!(module.is_empty());
|
||||
@ -285,10 +168,7 @@ mod tests {
|
||||
config: None,
|
||||
_name: name.to_string(),
|
||||
description: desc.to_string(),
|
||||
style: Style::default(),
|
||||
prefix: Affix::default_prefix(name),
|
||||
segments: vec![Segment::new("test_segment")],
|
||||
suffix: Affix::default_suffix(name),
|
||||
};
|
||||
|
||||
assert!(module.is_empty());
|
||||
|
@ -7,7 +7,8 @@ use std::str::FromStr;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::aws::{AwsConfig, AwsItems};
|
||||
use crate::configs::aws::AwsConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
type Profile = String;
|
||||
type Region = String;
|
||||
@ -51,73 +52,64 @@ fn get_aws_profile_and_region() -> (Option<Profile>, Option<Region>) {
|
||||
env::var("AWS_VAULT")
|
||||
.or_else(|_| env::var("AWS_PROFILE"))
|
||||
.ok(),
|
||||
env::var("AWS_REGION").ok(),
|
||||
env::var("AWS_DEFAULT_REGION").ok(),
|
||||
env::var("AWS_DEFAULT_REGION")
|
||||
.or_else(|_| env::var("AWS_REGION"))
|
||||
.ok(),
|
||||
) {
|
||||
(Some(p), Some(_), Some(dr)) => (Some(p), Some(dr)),
|
||||
(Some(p), Some(r), None) => (Some(p), Some(r)),
|
||||
(None, Some(r), None) => (None, Some(r)),
|
||||
(Some(p), None, Some(dr)) => (Some(p), Some(dr)),
|
||||
(Some(ref p), None, None) => (Some(p.to_owned()), get_aws_region_from_config(Some(p))),
|
||||
(None, None, Some(dr)) => (None, Some(dr)),
|
||||
(None, Some(_), Some(dr)) => (None, Some(dr)),
|
||||
(None, None, None) => (None, get_aws_region_from_config(None)),
|
||||
(Some(p), Some(r)) => (Some(p), Some(r)),
|
||||
(None, Some(r)) => (None, Some(r)),
|
||||
(Some(ref p), None) => (Some(p.to_owned()), get_aws_region_from_config(Some(p))),
|
||||
(None, None) => (None, get_aws_region_from_config(None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_aws_region() -> Option<Region> {
|
||||
match (
|
||||
env::var("AWS_REGION").ok(),
|
||||
env::var("AWS_DEFAULT_REGION").ok(),
|
||||
) {
|
||||
(Some(r), None) => Some(r),
|
||||
(None, Some(dr)) => Some(dr),
|
||||
(Some(_), Some(dr)) => Some(dr),
|
||||
(None, None) => get_aws_region_from_config(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn alias_region(region: &str, aliases: &HashMap<String, &str>) -> String {
|
||||
match aliases.get(region) {
|
||||
None => region.to_string(),
|
||||
fn alias_region(region: String, aliases: &HashMap<String, &str>) -> String {
|
||||
match aliases.get(®ion) {
|
||||
None => region,
|
||||
Some(alias) => (*alias).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
const AWS_PREFIX: &str = "on ";
|
||||
|
||||
let mut module = context.new_module("aws");
|
||||
let config: AwsConfig = AwsConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
let (aws_profile, aws_region) = get_aws_profile_and_region();
|
||||
if aws_profile.is_none() && aws_region.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
module.get_prefix().set_value(AWS_PREFIX);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
match config.displayed_items {
|
||||
AwsItems::All => {
|
||||
let (aws_profile, aws_region) = get_aws_profile_and_region();
|
||||
|
||||
let aws_segment = match (&aws_profile, &aws_region) {
|
||||
(None, None) => return None,
|
||||
(Some(p), Some(r)) => format!("{}({})", p, alias_region(r, &config.region_aliases)),
|
||||
(Some(p), None) => p.to_string(),
|
||||
(None, Some(r)) => alias_region(r, &config.region_aliases),
|
||||
};
|
||||
module.create_segment("all", &config.region.with_value(&aws_segment));
|
||||
}
|
||||
AwsItems::Profile => {
|
||||
let aws_profile = env::var("AWS_PROFILE").ok()?;
|
||||
|
||||
module.create_segment("profile", &config.profile.with_value(&aws_profile));
|
||||
}
|
||||
AwsItems::Region => {
|
||||
let aws_region = alias_region(&get_aws_region()?, &config.region_aliases);
|
||||
|
||||
module.create_segment("region", &config.region.with_value(&aws_region));
|
||||
}
|
||||
let mapped_region = if let Some(aws_region) = aws_region {
|
||||
Some(alias_region(aws_region, &config.region_aliases))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"profile" => aws_profile.as_ref().map(Ok),
|
||||
"region" => mapped_region.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::error!("Error in module `aws`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
use super::{Context, Module, RootModuleConfig, Shell};
|
||||
use crate::configs::battery::BatteryConfig;
|
||||
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module for the battery percentage and charging state
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
// TODO: Update when v1.0 printing refactor is implemented to only
|
||||
@ -14,60 +16,58 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let BatteryStatus { state, percentage } = battery_status;
|
||||
|
||||
let mut module = context.new_module("battery");
|
||||
let battery_config: BatteryConfig = BatteryConfig::try_load(module.config);
|
||||
let config: BatteryConfig = BatteryConfig::try_load(module.config);
|
||||
|
||||
// Parse config under `display`
|
||||
let display_styles = &battery_config.display;
|
||||
let display_style = display_styles
|
||||
// Parse config under `display`.
|
||||
// Select the first style that match the threshold,
|
||||
// if all thresholds are lower do not display battery module.
|
||||
let display_style = config
|
||||
.display
|
||||
.iter()
|
||||
.find(|display_style| percentage <= display_style.threshold as f32);
|
||||
.find(|display_style| percentage <= display_style.threshold as f32)?;
|
||||
|
||||
if let Some(display_style) = display_style {
|
||||
// Set style based on percentage
|
||||
module.set_style(display_style.style);
|
||||
module.get_prefix().set_value("");
|
||||
// Parse the format string and build the module
|
||||
match StringFormatter::new(config.format) {
|
||||
Ok(formatter) => {
|
||||
let formatter = formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => match state {
|
||||
battery::State::Full => Some(config.full_symbol),
|
||||
battery::State::Charging => Some(config.charging_symbol),
|
||||
battery::State::Discharging => Some(config.discharging_symbol),
|
||||
battery::State::Unknown => config.unknown_symbol,
|
||||
battery::State::Empty => config.empty_symbol,
|
||||
_ => {
|
||||
log::debug!("Unhandled battery state `{}`", state);
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|style| match style {
|
||||
"style" => Some(Ok(display_style.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"percentage" => Some(Ok(format!("{}{}", percentage.round(), percentage_char))),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
match state {
|
||||
battery::State::Full => {
|
||||
module.create_segment("full_symbol", &battery_config.full_symbol);
|
||||
}
|
||||
battery::State::Charging => {
|
||||
module.create_segment("charging_symbol", &battery_config.charging_symbol);
|
||||
}
|
||||
battery::State::Discharging => {
|
||||
module.create_segment("discharging_symbol", &battery_config.discharging_symbol);
|
||||
}
|
||||
battery::State::Unknown => {
|
||||
log::debug!("Unknown detected");
|
||||
if let Some(unknown_symbol) = battery_config.unknown_symbol {
|
||||
module.create_segment("unknown_symbol", &unknown_symbol);
|
||||
match formatter.parse(None) {
|
||||
Ok(format_string) => {
|
||||
module.set_segments(format_string);
|
||||
Some(module)
|
||||
}
|
||||
}
|
||||
battery::State::Empty => {
|
||||
if let Some(empty_symbol) = battery_config.empty_symbol {
|
||||
module.create_segment("empty_symbol", &empty_symbol);
|
||||
Err(e) => {
|
||||
log::warn!("Cannot parse `battery.format`: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
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.create_segment(
|
||||
"percentage",
|
||||
&battery_config
|
||||
.percentage
|
||||
.with_value(percent_string.join("").as_ref()),
|
||||
);
|
||||
|
||||
Some(module)
|
||||
} else {
|
||||
None
|
||||
Err(e) => {
|
||||
log::warn!("Cannot load `battery.format`: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
use super::{Context, Module, RootModuleConfig, Shell};
|
||||
use crate::configs::character::CharacterConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// 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)
|
||||
/// 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", it will be formatted with `success_symbol`
|
||||
/// (green arrow by default)
|
||||
/// - If the exit-code was anything else, it will be formatted with
|
||||
/// `error_symbol` (red arrow by default)
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
enum ShellEditMode {
|
||||
Normal,
|
||||
@ -19,12 +20,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
let mut module = context.new_module("character");
|
||||
let config: CharacterConfig = CharacterConfig::try_load(module.config);
|
||||
module.get_prefix().set_value("");
|
||||
|
||||
let props = &context.properties;
|
||||
let exit_code_default = std::string::String::from("0");
|
||||
let exit_code_default = String::from("0");
|
||||
let exit_code = props.get("status_code").unwrap_or(&exit_code_default);
|
||||
let keymap_default = std::string::String::from("viins");
|
||||
let keymap_default = String::from("viins");
|
||||
let keymap = props.get("keymap").unwrap_or(&keymap_default);
|
||||
let exit_success = exit_code == "0";
|
||||
|
||||
@ -38,22 +38,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
_ => ASSUMED_MODE,
|
||||
};
|
||||
|
||||
if exit_success {
|
||||
module.set_style(config.style_success);
|
||||
} else {
|
||||
module.set_style(config.style_failure);
|
||||
};
|
||||
|
||||
/* If an error symbol is set in the config, use symbols to indicate
|
||||
success/failure, in addition to color */
|
||||
if config.use_symbol_for_status && !exit_success {
|
||||
module.create_segment("error_symbol", &config.error_symbol)
|
||||
} else {
|
||||
match mode {
|
||||
ShellEditMode::Normal => module.create_segment("vicmd_symbol", &config.vicmd_symbol),
|
||||
ShellEditMode::Insert => module.create_segment("symbol", &config.symbol),
|
||||
let symbol = match mode {
|
||||
ShellEditMode::Normal => config.vicmd_symbol,
|
||||
ShellEditMode::Insert => {
|
||||
if exit_success {
|
||||
config.success_symbol
|
||||
} else {
|
||||
config.error_symbol
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(symbol),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `character`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::{Context, Module, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::cmd_duration::CmdDurationConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Outputs the time it took the last command to execute
|
||||
///
|
||||
@ -28,19 +28,30 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let config_min = config.min_time as u128;
|
||||
if elapsed < config.min_time as u128 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let module_color = match elapsed {
|
||||
time if time < config_min => return None,
|
||||
_ => config.style,
|
||||
};
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"duration" => Some(Ok(render_time(elapsed, config.show_milliseconds))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_style(module_color);
|
||||
module.create_segment(
|
||||
"cmd_duration",
|
||||
&SegmentConfig::new(&render_time(elapsed, config.show_milliseconds)),
|
||||
);
|
||||
module.get_prefix().set_value(config.prefix);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `cmd_duration`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use super::utils::directory::truncate;
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::conda::CondaConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the current Conda environment
|
||||
///
|
||||
@ -17,14 +17,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let mut module = context.new_module("conda");
|
||||
let config = CondaConfig::try_load(module.config);
|
||||
let config: CondaConfig = CondaConfig::try_load(module.config);
|
||||
|
||||
let conda_env = truncate(conda_env, config.truncation_length);
|
||||
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"environment" => Some(Ok(conda_env.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("environment", &config.environment.with_value(&conda_env));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `conda`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::crystal::CrystalConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Crystal version
|
||||
@ -20,14 +21,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let crystal_version = utils::exec_cmd("crystal", &["--version"])?.stdout;
|
||||
let formatted_version = format_crystal_version(&crystal_version)?;
|
||||
|
||||
let mut module = context.new_module("crystal");
|
||||
let config: CrystalConfig = CrystalConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => format_crystal_version(&crystal_version).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `crystal`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use ansi_term::Color;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Output, Stdio};
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::{config::SegmentConfig, configs::custom::CustomConfig};
|
||||
use crate::{configs::custom::CustomConfig, formatter::StringFormatter};
|
||||
|
||||
/// Creates a custom module with some configuration
|
||||
///
|
||||
@ -13,7 +12,7 @@ use crate::{config::SegmentConfig, configs::custom::CustomConfig};
|
||||
/// command can be run -- if its result is 0, the module will be shown.
|
||||
///
|
||||
/// Finally, the content of the module itself is also set by a command.
|
||||
pub fn module<'a>(name: &'a str, context: &'a Context) -> Option<Module<'a>> {
|
||||
pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||
let toml_config = context.config.get_custom_module_config(name).expect(
|
||||
"modules::custom::module should only be called after ensuring that the module exists",
|
||||
);
|
||||
@ -44,35 +43,42 @@ pub fn module<'a>(name: &'a str, context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let mut module = Module::new(name, config.description, Some(toml_config));
|
||||
let style = config.style.unwrap_or_else(|| Color::Green.bold());
|
||||
|
||||
if let Some(prefix) = config.prefix {
|
||||
module.get_prefix().set_value(prefix);
|
||||
}
|
||||
if let Some(suffix) = config.suffix {
|
||||
module.get_suffix().set_value(suffix);
|
||||
let output = exec_command(config.command, &config.shell.0)?;
|
||||
|
||||
let trimmed = output.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(symbol) = config.symbol {
|
||||
module.create_segment("symbol", &symbol);
|
||||
}
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
// This may result in multiple calls to `get_module_version` when a user have
|
||||
// multiple `$version` variables defined in `format`.
|
||||
"output" => Some(Ok(trimmed)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
if let Some(output) = exec_command(config.command, &config.shell.0) {
|
||||
let trimmed = output.trim();
|
||||
|
||||
if trimmed.is_empty() {
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `custom.{}`:\n{}", name, error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
module.create_segment(
|
||||
"output",
|
||||
&SegmentConfig::new(&trimmed).with_style(Some(style)),
|
||||
);
|
||||
|
||||
Some(module)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Some(module)
|
||||
}
|
||||
|
||||
/// Return the invoking shell, using `shell` and fallbacking in order to STARSHIP_SHELL and "sh"
|
||||
|
@ -7,8 +7,9 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use super::{Context, Module};
|
||||
|
||||
use super::utils::directory::truncate;
|
||||
use crate::config::{RootModuleConfig, SegmentConfig};
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::directory::DirectoryConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the current directory
|
||||
///
|
||||
@ -29,8 +30,6 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("directory");
|
||||
let config: DirectoryConfig = DirectoryConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
|
||||
// Using environment PWD is the standard approach for determining logical path
|
||||
// If this is None for any reason, we fall back to reading the os-provided path
|
||||
let physical_current_dir = if config.use_logical_path {
|
||||
@ -80,33 +79,39 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
// 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() {
|
||||
let fish_prefix = 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(¤t_dir, &home_dir, HOME_SYMBOL);
|
||||
let fish_style_dir = to_fish_style(
|
||||
to_fish_style(
|
||||
config.fish_style_pwd_dir_length as usize,
|
||||
contracted_home_dir,
|
||||
&truncated_dir_string,
|
||||
);
|
||||
)
|
||||
} else {
|
||||
String::from("")
|
||||
};
|
||||
let final_dir_string = format!("{}{}", fish_prefix, truncated_dir_string);
|
||||
|
||||
module.create_segment(
|
||||
"path",
|
||||
&SegmentConfig {
|
||||
value: &fish_style_dir,
|
||||
style: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"path" => Some(Ok(&final_dir_string)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment(
|
||||
"path",
|
||||
&SegmentConfig {
|
||||
value: &truncated_dir_string,
|
||||
style: None,
|
||||
},
|
||||
);
|
||||
|
||||
module.get_prefix().set_value(config.prefix);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `directory`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::docker_context::DockerContextConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
const DOCKER_CONFIG_FILE: &str = ".docker/config.json";
|
||||
|
||||
/// Creates a module with the currently active Docker context
|
||||
///
|
||||
/// Will display the Docker context if the following criteria are met:
|
||||
/// - There is a file named `$HOME/.docker/config.json`
|
||||
/// - Or a file named `$DOCKER_CONFIG/config.json`
|
||||
/// - The file is JSON and contains a field named `currentContext`
|
||||
/// - The value of `currentContext` is not `default`
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
@ -23,9 +26,17 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let docker_config = PathBuf::from(
|
||||
&env::var_os("DOCKER_CONFIG")
|
||||
.unwrap_or(dirs_next::home_dir()?.join(".docker").into_os_string()),
|
||||
)
|
||||
.join("config.json");
|
||||
|
||||
let config_path = dirs_next::home_dir()?.join(DOCKER_CONFIG_FILE);
|
||||
let json = utils::read_file(config_path).ok()?;
|
||||
if !docker_config.exists() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let json = utils::read_file(docker_config).ok()?;
|
||||
let parsed_json = serde_json::from_str(&json).ok()?;
|
||||
|
||||
match parsed_json {
|
||||
@ -33,9 +44,31 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let current_context = root.get("currentContext")?;
|
||||
match current_context {
|
||||
serde_json::Value::String(ctx) => {
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("context", &config.context.with_value(&ctx));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"context" => Some(Ok(ctx)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `docker_context`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
_ => None,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use quick_xml::events::Event;
|
||||
use quick_xml::Reader;
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::Iterator;
|
||||
use std::ops::Deref;
|
||||
@ -6,6 +8,7 @@ use std::str;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
use crate::configs::dotnet::DotnetConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
type JValue = serde_json::Value;
|
||||
@ -48,20 +51,90 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
// Internally, this module uses its own mechanism for version detection.
|
||||
// Typically it is twice as fast as running `dotnet --version`.
|
||||
let enable_heuristic = config.heuristic;
|
||||
let version = if enable_heuristic {
|
||||
let repo_root = context.get_repo().ok().and_then(|r| r.root.as_deref());
|
||||
estimate_dotnet_version(&dotnet_files, &context.current_dir, repo_root)?
|
||||
} else {
|
||||
get_version_from_cli()?
|
||||
};
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &config.version.with_value(&version.0));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"symbol" => Some(Ok(config.symbol)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => {
|
||||
let version = if enable_heuristic {
|
||||
let repo_root = context.get_repo().ok().and_then(|r| r.root.as_deref());
|
||||
estimate_dotnet_version(&dotnet_files, &context.current_dir, repo_root)
|
||||
} else {
|
||||
get_version_from_cli()
|
||||
};
|
||||
version.map(|v| Ok(v.0))
|
||||
}
|
||||
"tfm" => find_current_tfm(&dotnet_files).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `dotnet`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn find_current_tfm<'a>(files: &[DotNetFile<'a>]) -> Option<String> {
|
||||
let get_file_of_type = |t: FileType| files.iter().find(|f| f.file_type == t);
|
||||
|
||||
let relevant_file = get_file_of_type(FileType::ProjectFile)?;
|
||||
|
||||
get_tfm_from_project_file(relevant_file.path)
|
||||
}
|
||||
|
||||
fn get_tfm_from_project_file(path: &Path) -> Option<String> {
|
||||
let project_file = utils::read_file(path).ok()?;
|
||||
let mut reader = Reader::from_str(&project_file);
|
||||
reader.trim_text(true);
|
||||
|
||||
let mut in_tfm = false;
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
// for triggering namespaced events, use this instead:
|
||||
// match reader.read_namespaced_event(&mut buf) {
|
||||
Ok(Event::Start(ref e)) => {
|
||||
// for namespaced:
|
||||
// Ok((ref namespace_value, Event::Start(ref e)))
|
||||
match e.name() {
|
||||
b"TargetFrameworks" => in_tfm = true,
|
||||
b"TargetFramework" => in_tfm = true,
|
||||
_ => in_tfm = false,
|
||||
}
|
||||
}
|
||||
// unescape and decode the text event using the reader encoding
|
||||
Ok(Event::Text(e)) => {
|
||||
if in_tfm {
|
||||
return e.unescape_and_decode(&reader).ok();
|
||||
}
|
||||
}
|
||||
Ok(Event::Eof) => break, // exits the loop when reaching end of file
|
||||
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
|
||||
_ => (), // There are several other `Event`s we do not consider here
|
||||
}
|
||||
|
||||
// if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
|
||||
buf.clear();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn estimate_dotnet_version<'a>(
|
||||
files: &[DotNetFile<'a>],
|
||||
current_dir: &Path,
|
||||
|
@ -1,9 +1,10 @@
|
||||
use regex::Regex;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::elixir::ElixirConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
use regex::Regex;
|
||||
const ELIXIR_VERSION_PATTERN: &str = "\
|
||||
Erlang/OTP (?P<otp>\\d+)[^\\n]+
|
||||
|
||||
@ -11,7 +12,7 @@ Elixir (?P<elixir>\\d[.\\d]+).*";
|
||||
|
||||
/// Create a module with the current Elixir version
|
||||
///
|
||||
/// Will display the Rust version if any of the following criteria are met:
|
||||
/// Will display the Elixir version if any of the following criteria are met:
|
||||
/// - Current directory contains a `mix.exs` file
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let is_elixir_project = context.try_begin_scan()?.set_files(&["mix.exs"]).is_match();
|
||||
@ -24,23 +25,36 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
let mut module = context.new_module("elixir");
|
||||
let config = ElixirConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(&elixir_version)),
|
||||
"otp_version" => Some(Ok(&otp_version)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &config.version.with_value(&elixir_version));
|
||||
module.create_segment(
|
||||
"otp_version",
|
||||
&config
|
||||
.otp_version
|
||||
.with_value(&format!(" (OTP {})", otp_version)),
|
||||
);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `elixir`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_elixir_version() -> Option<(String, String)> {
|
||||
use crate::utils;
|
||||
|
||||
let output = utils::exec_cmd("elixir", &["--version"])?.stdout;
|
||||
|
||||
parse_elixir_version(&output)
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::elm::ElmConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Elm version
|
||||
@ -24,14 +25,35 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let elm_version = utils::exec_cmd("elm", &["--version"])?.stdout;
|
||||
let formatted_version = Some(format!("v{}", elm_version.trim()))?;
|
||||
let module_version = Some(format!("v{}", elm_version.trim()))?;
|
||||
|
||||
let mut module = context.new_module("elm");
|
||||
let config: ElmConfig = ElmConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(&module_version)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `elm`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module, SegmentConfig};
|
||||
use super::{Context, Module};
|
||||
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::env_var::EnvVarConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the value of the chosen environment variable
|
||||
///
|
||||
@ -16,17 +17,30 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let config: EnvVarConfig = EnvVarConfig::try_load(module.config);
|
||||
|
||||
let env_value = get_env_value(config.variable?, config.default)?;
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"env_value" => Some(Ok(&env_value)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_style(config.style);
|
||||
module.get_prefix().set_value("with ");
|
||||
|
||||
if let Some(symbol) = config.symbol {
|
||||
module.create_segment("symbol", &symbol);
|
||||
}
|
||||
|
||||
// TODO: Use native prefix and suffix instead of stacking custom ones together with env_value.
|
||||
let env_var_stacked = format!("{}{}{}", config.prefix, env_value, config.suffix);
|
||||
module.create_segment("env_var", &SegmentConfig::new(&env_var_stacked));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `env_var`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::erlang::ErlangConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Create a module with the current Erlang version
|
||||
///
|
||||
@ -17,14 +18,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let erlang_version = get_erlang_version()?;
|
||||
|
||||
let mut module = context.new_module("erlang");
|
||||
let config = ErlangConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &config.version.with_value(&erlang_version));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => get_erlang_version().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `erlang`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::git_branch::GitBranchConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the Git branch in the current directory
|
||||
///
|
||||
@ -10,15 +11,11 @@ use crate::configs::git_branch::GitBranchConfig;
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("git_branch");
|
||||
let config = GitBranchConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.get_prefix().set_value("on ");
|
||||
|
||||
let truncation_symbol = get_graphemes(config.truncation_symbol, 1);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
let truncation_symbol = get_first_grapheme(config.truncation_symbol);
|
||||
|
||||
// TODO: Once error handling is implemented, warn the user if their config
|
||||
// truncation length is nonsensical
|
||||
// truncation length is nonsensical
|
||||
let len = if config.truncation_length <= 0 {
|
||||
log::warn!(
|
||||
"\"truncation_length\" should be a positive value, found {}",
|
||||
@ -31,29 +28,50 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
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.create_segment(
|
||||
"name",
|
||||
&config.branch_name.with_value(&truncated_and_symbol),
|
||||
);
|
||||
let mut graphemes = get_graphemes(&branch_name);
|
||||
let trunc_len = len.min(graphemes.len());
|
||||
|
||||
if trunc_len < graphemes.len() {
|
||||
// The truncation symbol should only be added if we truncate
|
||||
graphemes[trunc_len] = truncation_symbol;
|
||||
graphemes.truncate(trunc_len + 1)
|
||||
}
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"branch" => Some(Ok(graphemes.concat())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `git_branch`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_graphemes(text: &str, length: usize) -> String {
|
||||
fn get_first_grapheme(text: &str) -> &str {
|
||||
UnicodeSegmentation::graphemes(text, true)
|
||||
.take(length)
|
||||
.collect::<Vec<&str>>()
|
||||
.concat()
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
}
|
||||
|
||||
fn graphemes_len(text: &str) -> usize {
|
||||
UnicodeSegmentation::graphemes(&text[..], true).count()
|
||||
fn get_graphemes(text: &str) -> Vec<&str> {
|
||||
UnicodeSegmentation::graphemes(text, true).collect()
|
||||
}
|
||||
|
@ -2,23 +2,14 @@ use super::{Context, Module, RootModuleConfig};
|
||||
use git2::Repository;
|
||||
|
||||
use crate::configs::git_commit::GitCommitConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the Git commit in the current directory
|
||||
///
|
||||
/// Will display the commit hash if the current directory is a git repo
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("git_commit");
|
||||
let config = GitCommitConfig::try_load(module.config);
|
||||
|
||||
module
|
||||
.get_prefix()
|
||||
.set_value(config.prefix)
|
||||
.set_style(config.style);
|
||||
module
|
||||
.get_suffix()
|
||||
.set_value(config.suffix)
|
||||
.set_style(config.style);
|
||||
module.set_style(config.style);
|
||||
let config: GitCommitConfig = GitCommitConfig::try_load(module.config);
|
||||
|
||||
let repo = context.get_repo().ok()?;
|
||||
let repo_root = repo.root.as_ref()?;
|
||||
@ -32,13 +23,30 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let git_head = git_repo.head().ok()?;
|
||||
let head_commit = git_head.peel_to_commit().ok()?;
|
||||
let commit_oid = head_commit.id();
|
||||
module.create_segment(
|
||||
"hash",
|
||||
&config.hash.with_value(&id_to_hex_abbrev(
|
||||
commit_oid.as_bytes(),
|
||||
config.commit_hash_length,
|
||||
)),
|
||||
);
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"hash" => Some(Ok(id_to_hex_abbrev(
|
||||
commit_oid.as_bytes(),
|
||||
config.commit_hash_length,
|
||||
))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `git_commit`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use git2::RepositoryState;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
use crate::configs::git_state::GitStateConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the state of the git repository at the current directory
|
||||
///
|
||||
@ -12,37 +13,37 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("git_state");
|
||||
let config: GitStateConfig = GitStateConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
module.get_prefix().set_value("(");
|
||||
module.get_suffix().set_value(") ");
|
||||
|
||||
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, config);
|
||||
let state_description = get_state_description(repo_state, repo_root, &config)?;
|
||||
|
||||
let label = match &state_description {
|
||||
StateDescription::Label(label) => label,
|
||||
StateDescription::LabelAndProgress(label, _) => label,
|
||||
StateDescription::Clean => {
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"state" => Some(state_description.label),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"progress_current" => state_description.current.as_ref().map(Ok),
|
||||
"progress_total" => state_description.total.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `git_state`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
module.create_segment(label.name, &label.segment);
|
||||
|
||||
if let StateDescription::LabelAndProgress(_, progress) = &state_description {
|
||||
module.create_segment(
|
||||
"progress_current",
|
||||
&SegmentConfig::new(&format!(" {}", progress.current)),
|
||||
);
|
||||
module.create_segment("progress_divider", &SegmentConfig::new("/"));
|
||||
module.create_segment(
|
||||
"progress_total",
|
||||
&SegmentConfig::new(&format!("{}", progress.total)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
@ -53,40 +54,57 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
fn get_state_description<'a>(
|
||||
state: RepositoryState,
|
||||
root: &'a std::path::PathBuf,
|
||||
config: GitStateConfig<'a>,
|
||||
) -> StateDescription<'a> {
|
||||
config: &GitStateConfig<'a>,
|
||||
) -> Option<StateDescription<'a>> {
|
||||
match state {
|
||||
RepositoryState::Clean => StateDescription::Clean,
|
||||
RepositoryState::Merge => StateDescription::Label(StateLabel::new("merge", config.merge)),
|
||||
RepositoryState::Revert => {
|
||||
StateDescription::Label(StateLabel::new("revert", config.revert))
|
||||
}
|
||||
RepositoryState::RevertSequence => {
|
||||
StateDescription::Label(StateLabel::new("revert", config.revert))
|
||||
}
|
||||
RepositoryState::CherryPick => {
|
||||
StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
|
||||
}
|
||||
RepositoryState::CherryPickSequence => {
|
||||
StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
|
||||
}
|
||||
RepositoryState::Bisect => {
|
||||
StateDescription::Label(StateLabel::new("bisect", config.bisect))
|
||||
}
|
||||
RepositoryState::ApplyMailbox => StateDescription::Label(StateLabel::new("am", config.am)),
|
||||
RepositoryState::ApplyMailboxOrRebase => {
|
||||
StateDescription::Label(StateLabel::new("am_or_rebase", config.am_or_rebase))
|
||||
}
|
||||
RepositoryState::Rebase => describe_rebase(root, config.rebase),
|
||||
RepositoryState::RebaseInteractive => describe_rebase(root, config.rebase),
|
||||
RepositoryState::RebaseMerge => describe_rebase(root, config.rebase),
|
||||
RepositoryState::Clean => None,
|
||||
RepositoryState::Merge => Some(StateDescription {
|
||||
label: config.merge,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Revert => Some(StateDescription {
|
||||
label: config.revert,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::RevertSequence => Some(StateDescription {
|
||||
label: config.revert,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::CherryPick => Some(StateDescription {
|
||||
label: config.cherry_pick,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::CherryPickSequence => Some(StateDescription {
|
||||
label: config.cherry_pick,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Bisect => Some(StateDescription {
|
||||
label: config.bisect,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::ApplyMailbox => Some(StateDescription {
|
||||
label: config.am,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::ApplyMailboxOrRebase => Some(StateDescription {
|
||||
label: config.am_or_rebase,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Rebase => Some(describe_rebase(root, config.rebase)),
|
||||
RepositoryState::RebaseInteractive => Some(describe_rebase(root, config.rebase)),
|
||||
RepositoryState::RebaseMerge => Some(describe_rebase(root, config.rebase)),
|
||||
}
|
||||
}
|
||||
|
||||
fn describe_rebase<'a>(
|
||||
root: &'a PathBuf,
|
||||
rebase_config: SegmentConfig<'a>,
|
||||
) -> StateDescription<'a> {
|
||||
fn describe_rebase<'a>(root: &'a PathBuf, rebase_config: &'a str) -> StateDescription<'a> {
|
||||
/*
|
||||
* Sadly, libgit2 seems to have some issues with reading the state of
|
||||
* interactive rebases. So, instead, we'll poke a few of the .git files
|
||||
@ -98,12 +116,12 @@ fn describe_rebase<'a>(
|
||||
let dot_git = root.join(".git");
|
||||
|
||||
let has_path = |relative_path: &str| {
|
||||
let path = dot_git.join(Path::new(relative_path));
|
||||
let path = dot_git.join(PathBuf::from(relative_path));
|
||||
path.exists()
|
||||
};
|
||||
|
||||
let file_to_usize = |relative_path: &str| {
|
||||
let path = dot_git.join(Path::new(relative_path));
|
||||
let path = dot_git.join(PathBuf::from(relative_path));
|
||||
let contents = crate::utils::read_file(path).ok()?;
|
||||
let quantity = contents.trim().parse::<usize>().ok()?;
|
||||
Some(quantity)
|
||||
@ -112,7 +130,7 @@ fn describe_rebase<'a>(
|
||||
let paths_to_progress = |current_path: &str, total_path: &str| {
|
||||
let current = file_to_usize(current_path)?;
|
||||
let total = file_to_usize(total_path)?;
|
||||
Some(StateProgress { current, total })
|
||||
Some((current, total))
|
||||
};
|
||||
|
||||
let progress = if has_path("rebase-merge") {
|
||||
@ -123,32 +141,15 @@ fn describe_rebase<'a>(
|
||||
None
|
||||
};
|
||||
|
||||
match progress {
|
||||
None => StateDescription::Label(StateLabel::new("rebase", rebase_config)),
|
||||
Some(progress) => {
|
||||
StateDescription::LabelAndProgress(StateLabel::new("rebase", rebase_config), progress)
|
||||
}
|
||||
StateDescription {
|
||||
label: rebase_config,
|
||||
current: Some(format!("{}", progress.unwrap().0)),
|
||||
total: Some(format!("{}", progress.unwrap().1)),
|
||||
}
|
||||
}
|
||||
|
||||
enum StateDescription<'a> {
|
||||
Clean,
|
||||
Label(StateLabel<'a>),
|
||||
LabelAndProgress(StateLabel<'a>, StateProgress),
|
||||
}
|
||||
|
||||
struct StateLabel<'a> {
|
||||
name: &'static str,
|
||||
segment: SegmentConfig<'a>,
|
||||
}
|
||||
|
||||
struct StateProgress {
|
||||
current: usize,
|
||||
total: usize,
|
||||
}
|
||||
|
||||
impl<'a> StateLabel<'a> {
|
||||
fn new(name: &'static str, segment: SegmentConfig<'a>) -> Self {
|
||||
Self { name, segment }
|
||||
}
|
||||
struct StateDescription<'a> {
|
||||
label: &'a str,
|
||||
current: Option<String>,
|
||||
total: Option<String>,
|
||||
}
|
||||
|
@ -2,10 +2,13 @@ use git2::{Repository, Status};
|
||||
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::config::SegmentConfig;
|
||||
use crate::configs::git_status::{CountConfig, GitStatusConfig};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::collections::HashMap;
|
||||
use crate::configs::git_status::GitStatusConfig;
|
||||
use crate::context::Repo;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::segment::Segment;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
const ALL_STATUS_FORMAT: &str = "$conflicted$stashed$deleted$renamed$modified$staged$untracked";
|
||||
|
||||
/// Creates a module with the Git branch in the current directory
|
||||
///
|
||||
@ -23,174 +26,233 @@ use std::collections::HashMap;
|
||||
/// - `✘` — A file's deletion has been added to the staging area
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let repo = context.get_repo().ok()?;
|
||||
let branch_name = repo.branch.as_ref()?;
|
||||
let repo_root = repo.root.as_ref()?;
|
||||
let mut repository = Repository::open(repo_root).ok()?;
|
||||
let info = Arc::new(GitStatusInfo::load(repo));
|
||||
|
||||
let mut module = context.new_module("git_status");
|
||||
let config: GitStatusConfig = GitStatusConfig::try_load(module.config);
|
||||
|
||||
module
|
||||
.get_prefix()
|
||||
.set_value(config.prefix)
|
||||
.set_style(config.style);
|
||||
module
|
||||
.get_suffix()
|
||||
.set_value(config.suffix)
|
||||
.set_style(config.style);
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"all_status" => Some(ALL_STATUS_FORMAT),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable: &str| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map_variables_to_segments(|variable: &str| {
|
||||
let info = Arc::clone(&info);
|
||||
let segments = match variable {
|
||||
"stashed" => info.get_stashed().and_then(|count| {
|
||||
format_count(config.stashed, "git_status.stashed", count)
|
||||
}),
|
||||
"ahead_behind" => info.get_ahead_behind().and_then(|(ahead, behind)| {
|
||||
if ahead > 0 && behind > 0 {
|
||||
format_text(config.diverged, "git_status.diverged", |variable| {
|
||||
match variable {
|
||||
"ahead_count" => Some(ahead.to_string()),
|
||||
"behind_count" => Some(behind.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
} else if ahead > 0 && behind == 0 {
|
||||
format_count(config.ahead, "git_status.ahead", ahead)
|
||||
} else if behind > 0 && ahead == 0 {
|
||||
format_count(config.behind, "git_status.behind", behind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
"conflicted" => info.get_conflicted().and_then(|count| {
|
||||
format_count(config.conflicted, "git_status.conflicted", count)
|
||||
}),
|
||||
"deleted" => info.get_deleted().and_then(|count| {
|
||||
format_count(config.deleted, "git_status.deleted", count)
|
||||
}),
|
||||
"renamed" => info.get_renamed().and_then(|count| {
|
||||
format_count(config.renamed, "git_status.renamed", count)
|
||||
}),
|
||||
"modified" => info.get_modified().and_then(|count| {
|
||||
format_count(config.modified, "git_status.modified", count)
|
||||
}),
|
||||
"staged" => info
|
||||
.get_staged()
|
||||
.and_then(|count| format_count(config.staged, "git_status.staged", count)),
|
||||
"untracked" => info.get_untracked().and_then(|count| {
|
||||
format_count(config.untracked, "git_status.untracked", count)
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
segments.map(Ok)
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
let repo_status = get_repo_status(repository.borrow_mut());
|
||||
log::debug!("Repo status: {:?}", repo_status);
|
||||
|
||||
let ahead_behind = get_ahead_behind(&repository, branch_name);
|
||||
if ahead_behind == Ok((0, 0)) {
|
||||
log::trace!("No ahead/behind found");
|
||||
} else {
|
||||
log::debug!("Repo ahead/behind: {:?}", ahead_behind);
|
||||
}
|
||||
|
||||
// Add the conflicted segment
|
||||
if let Ok(repo_status) = repo_status {
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"conflicted",
|
||||
repo_status.conflicted,
|
||||
&config.conflicted,
|
||||
config.conflicted_count,
|
||||
);
|
||||
}
|
||||
|
||||
// Add the ahead/behind segment
|
||||
if let Ok((ahead, behind)) = ahead_behind {
|
||||
let add_ahead = |m: &mut Module<'a>| {
|
||||
create_segment_with_count(
|
||||
m,
|
||||
"ahead",
|
||||
ahead,
|
||||
&config.ahead,
|
||||
CountConfig {
|
||||
enabled: config.show_sync_count,
|
||||
style: None,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let add_behind = |m: &mut Module<'a>| {
|
||||
create_segment_with_count(
|
||||
m,
|
||||
"behind",
|
||||
behind,
|
||||
&config.behind,
|
||||
CountConfig {
|
||||
enabled: config.show_sync_count,
|
||||
style: None,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
if ahead > 0 && behind > 0 {
|
||||
module.create_segment("diverged", &config.diverged);
|
||||
|
||||
if config.show_sync_count {
|
||||
add_ahead(&mut module);
|
||||
add_behind(&mut module);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => {
|
||||
if segments.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
segments
|
||||
}
|
||||
}
|
||||
|
||||
if ahead > 0 && behind == 0 {
|
||||
add_ahead(&mut module);
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `git_status`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
|
||||
if behind > 0 && ahead == 0 {
|
||||
add_behind(&mut module);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the stashed segment
|
||||
if let Ok(repo_status) = repo_status {
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"stashed",
|
||||
repo_status.stashed,
|
||||
&config.stashed,
|
||||
config.stashed_count,
|
||||
);
|
||||
}
|
||||
|
||||
// Add all remaining status segments
|
||||
if let Ok(repo_status) = repo_status {
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"deleted",
|
||||
repo_status.deleted,
|
||||
&config.deleted,
|
||||
config.deleted_count,
|
||||
);
|
||||
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"renamed",
|
||||
repo_status.renamed,
|
||||
&config.renamed,
|
||||
config.renamed_count,
|
||||
);
|
||||
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"modified",
|
||||
repo_status.modified,
|
||||
&config.modified,
|
||||
config.modified_count,
|
||||
);
|
||||
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"staged",
|
||||
repo_status.staged,
|
||||
&config.staged,
|
||||
config.staged_count,
|
||||
);
|
||||
|
||||
create_segment_with_count(
|
||||
&mut module,
|
||||
"untracked",
|
||||
repo_status.untracked,
|
||||
&config.untracked,
|
||||
config.untracked_count,
|
||||
);
|
||||
}
|
||||
|
||||
if module.is_empty() {
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn create_segment_with_count<'a>(
|
||||
module: &mut Module<'a>,
|
||||
name: &str,
|
||||
count: usize,
|
||||
config: &SegmentConfig<'a>,
|
||||
count_config: CountConfig,
|
||||
) {
|
||||
if count > 0 {
|
||||
module.create_segment(name, &config);
|
||||
struct GitStatusInfo<'a> {
|
||||
repo: &'a Repo,
|
||||
ahead_behind: RwLock<Option<Result<(usize, usize), git2::Error>>>,
|
||||
repo_status: RwLock<Option<Result<RepoStatus, git2::Error>>>,
|
||||
stashed_count: RwLock<Option<Result<usize, git2::Error>>>,
|
||||
}
|
||||
|
||||
if count_config.enabled {
|
||||
module.create_segment(
|
||||
&format!("{}_count", name),
|
||||
&SegmentConfig::new(&count.to_string()).with_style(count_config.style),
|
||||
);
|
||||
impl<'a> GitStatusInfo<'a> {
|
||||
pub fn load(repo: &'a Repo) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
ahead_behind: RwLock::new(None),
|
||||
repo_status: RwLock::new(None),
|
||||
stashed_count: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_branch_name(&self) -> String {
|
||||
self.repo
|
||||
.branch
|
||||
.clone()
|
||||
.unwrap_or_else(|| String::from("master"))
|
||||
}
|
||||
|
||||
fn get_repository(&self) -> Option<Repository> {
|
||||
// bare repos don't have a branch name, so `repo.branch.as_ref` would return None,
|
||||
// but git treats "master" as the default branch name
|
||||
let repo_root = self.repo.root.as_ref()?;
|
||||
Repository::open(repo_root).ok()
|
||||
}
|
||||
|
||||
pub fn get_ahead_behind(&self) -> Option<(usize, usize)> {
|
||||
{
|
||||
let data = self.ahead_behind.read().unwrap();
|
||||
if let Some(result) = data.as_ref() {
|
||||
return match result.as_ref() {
|
||||
Ok(ahead_behind) => Some(*ahead_behind),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_ahead_behind: {}", error);
|
||||
None
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let repo = self.get_repository()?;
|
||||
let branch_name = self.get_branch_name();
|
||||
let mut data = self.ahead_behind.write().unwrap();
|
||||
*data = Some(get_ahead_behind(&repo, &branch_name));
|
||||
match data.as_ref().unwrap() {
|
||||
Ok(ahead_behind) => Some(*ahead_behind),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_ahead_behind: {}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_repo_status(&self) -> Option<RepoStatus> {
|
||||
{
|
||||
let data = self.repo_status.read().unwrap();
|
||||
if let Some(result) = data.as_ref() {
|
||||
return match result.as_ref() {
|
||||
Ok(repo_status) => Some(*repo_status),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_repo_status: {}", error);
|
||||
None
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let mut repo = self.get_repository()?;
|
||||
let mut data = self.repo_status.write().unwrap();
|
||||
*data = Some(get_repo_status(&mut repo));
|
||||
match data.as_ref().unwrap() {
|
||||
Ok(repo_status) => Some(*repo_status),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_repo_status: {}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_stashed(&self) -> Option<usize> {
|
||||
{
|
||||
let data = self.stashed_count.read().unwrap();
|
||||
if let Some(result) = data.as_ref() {
|
||||
return match result.as_ref() {
|
||||
Ok(stashed_count) => Some(*stashed_count),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_stashed_count: {}", error);
|
||||
None
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
let mut repo = self.get_repository()?;
|
||||
let mut data = self.stashed_count.write().unwrap();
|
||||
*data = Some(get_stashed_count(&mut repo));
|
||||
match data.as_ref().unwrap() {
|
||||
Ok(stashed_count) => Some(*stashed_count),
|
||||
Err(error) => {
|
||||
log::warn!("Warn: get_stashed_count: {}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_conflicted(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.conflicted)
|
||||
}
|
||||
|
||||
pub fn get_deleted(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.deleted)
|
||||
}
|
||||
|
||||
pub fn get_renamed(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.renamed)
|
||||
}
|
||||
|
||||
pub fn get_modified(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.modified)
|
||||
}
|
||||
|
||||
pub fn get_staged(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.staged)
|
||||
}
|
||||
|
||||
pub fn get_untracked(&self) -> Option<usize> {
|
||||
self.get_repo_status().map(|data| data.untracked)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the number of files in various git states (staged, modified, deleted, etc...)
|
||||
fn get_repo_status(repository: &mut Repository) -> Result<RepoStatus, git2::Error> {
|
||||
let mut status_options = git2::StatusOptions::new();
|
||||
|
||||
let mut repo_status = RepoStatus::default();
|
||||
|
||||
match repository.config()?.get_entry("status.showUntrackedFiles") {
|
||||
Ok(entry) => status_options.include_untracked(entry.value() != Some("no")),
|
||||
_ => status_options.include_untracked(true),
|
||||
@ -201,76 +263,21 @@ fn get_repo_status(repository: &mut Repository) -> Result<RepoStatus, git2::Erro
|
||||
.renames_index_to_workdir(true)
|
||||
.include_unmodified(true);
|
||||
|
||||
let statuses: Vec<Status> = repository
|
||||
.statuses(Some(&mut status_options))?
|
||||
.iter()
|
||||
.map(|s| s.status())
|
||||
.collect();
|
||||
let statuses = repository.statuses(Some(&mut status_options))?;
|
||||
|
||||
if statuses.is_empty() {
|
||||
return Err(git2::Error::from_str("Repo has no status"));
|
||||
}
|
||||
|
||||
let statuses_count = count_statuses(statuses);
|
||||
|
||||
let repo_status: RepoStatus = RepoStatus {
|
||||
conflicted: *statuses_count.get("conflicted").unwrap_or(&0),
|
||||
deleted: *statuses_count.get("deleted").unwrap_or(&0),
|
||||
renamed: *statuses_count.get("renamed").unwrap_or(&0),
|
||||
modified: *statuses_count.get("modified").unwrap_or(&0),
|
||||
staged: *statuses_count.get("staged").unwrap_or(&0),
|
||||
untracked: *statuses_count.get("untracked").unwrap_or(&0),
|
||||
stashed: stashed_count(repository)?,
|
||||
};
|
||||
statuses
|
||||
.iter()
|
||||
.map(|s| s.status())
|
||||
.for_each(|status| repo_status.add(status));
|
||||
|
||||
Ok(repo_status)
|
||||
}
|
||||
|
||||
fn count_statuses(statuses: Vec<Status>) -> HashMap<&'static str, usize> {
|
||||
let mut predicates: HashMap<&'static str, fn(git2::Status) -> bool> = HashMap::new();
|
||||
predicates.insert("conflicted", is_conflicted);
|
||||
predicates.insert("deleted", is_deleted);
|
||||
predicates.insert("renamed", is_renamed);
|
||||
predicates.insert("modified", is_modified);
|
||||
predicates.insert("staged", is_staged);
|
||||
predicates.insert("untracked", is_untracked);
|
||||
|
||||
statuses.iter().fold(HashMap::new(), |mut map, status| {
|
||||
for (key, predicate) in predicates.iter() {
|
||||
if predicate(*status) {
|
||||
let entry = map.entry(key).or_insert(0);
|
||||
*entry += 1;
|
||||
}
|
||||
}
|
||||
map
|
||||
})
|
||||
}
|
||||
|
||||
fn is_conflicted(status: Status) -> bool {
|
||||
status.is_conflicted()
|
||||
}
|
||||
|
||||
fn is_deleted(status: Status) -> bool {
|
||||
status.is_wt_deleted() || status.is_index_deleted()
|
||||
}
|
||||
|
||||
fn is_renamed(status: Status) -> bool {
|
||||
status.is_wt_renamed() || status.is_index_renamed()
|
||||
}
|
||||
|
||||
fn is_modified(status: Status) -> bool {
|
||||
status.is_wt_modified()
|
||||
}
|
||||
|
||||
fn is_staged(status: Status) -> bool {
|
||||
status.is_index_modified() || status.is_index_new()
|
||||
}
|
||||
|
||||
fn is_untracked(status: Status) -> bool {
|
||||
status.is_wt_new()
|
||||
}
|
||||
|
||||
fn stashed_count(repository: &mut Repository) -> Result<usize, git2::Error> {
|
||||
fn get_stashed_count(repository: &mut Repository) -> Result<usize, git2::Error> {
|
||||
let mut count = 0;
|
||||
repository.stash_foreach(|_, _, _| {
|
||||
count += 1;
|
||||
@ -303,5 +310,65 @@ struct RepoStatus {
|
||||
modified: usize,
|
||||
staged: usize,
|
||||
untracked: usize,
|
||||
stashed: usize,
|
||||
}
|
||||
|
||||
impl RepoStatus {
|
||||
fn is_conflicted(status: Status) -> bool {
|
||||
status.is_conflicted()
|
||||
}
|
||||
|
||||
fn is_deleted(status: Status) -> bool {
|
||||
status.is_wt_deleted() || status.is_index_deleted()
|
||||
}
|
||||
|
||||
fn is_renamed(status: Status) -> bool {
|
||||
status.is_wt_renamed() || status.is_index_renamed()
|
||||
}
|
||||
|
||||
fn is_modified(status: Status) -> bool {
|
||||
status.is_wt_modified()
|
||||
}
|
||||
|
||||
fn is_staged(status: Status) -> bool {
|
||||
status.is_index_modified() || status.is_index_new()
|
||||
}
|
||||
|
||||
fn is_untracked(status: Status) -> bool {
|
||||
status.is_wt_new()
|
||||
}
|
||||
|
||||
fn add(&mut self, s: Status) {
|
||||
self.conflicted += RepoStatus::is_conflicted(s) as usize;
|
||||
self.deleted += RepoStatus::is_deleted(s) as usize;
|
||||
self.renamed += RepoStatus::is_renamed(s) as usize;
|
||||
self.modified += RepoStatus::is_modified(s) as usize;
|
||||
self.staged += RepoStatus::is_staged(s) as usize;
|
||||
self.untracked += RepoStatus::is_untracked(s) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
fn format_text<F>(format_str: &str, config_path: &str, mapper: F) -> Option<Vec<Segment>>
|
||||
where
|
||||
F: Fn(&str) -> Option<String> + Send + Sync,
|
||||
{
|
||||
if let Ok(formatter) = StringFormatter::new(format_str) {
|
||||
formatter
|
||||
.map(|variable| mapper(variable).map(Ok))
|
||||
.parse(None)
|
||||
.ok()
|
||||
} else {
|
||||
log::error!("Error parsing format string `{}`", &config_path);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn format_count(format_str: &str, config_path: &str, count: usize) -> Option<Vec<Segment>> {
|
||||
if count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
format_text(format_str, config_path, |variable| match variable {
|
||||
"count" => Some(count.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::go::GoConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Go version
|
||||
@ -34,14 +35,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let mut module = context.new_module("golang");
|
||||
let config: GoConfig = GoConfig::try_load(module.config);
|
||||
let config = GoConfig::try_load(module.config);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => {
|
||||
format_go_version(&utils::exec_cmd("go", &["version"])?.stdout.as_str()).map(Ok)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
|
||||
let formatted_version =
|
||||
format_go_version(&utils::exec_cmd("go", &["version"])?.stdout.as_str())?;
|
||||
module.create_segment("version", &config.version.with_value(&formatted_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `golang`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::hg_branch::HgBranchConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the Hg bookmark or branch in the current directory
|
||||
///
|
||||
@ -15,12 +16,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let mut module = context.new_module("hg_branch");
|
||||
let config = HgBranchConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.get_prefix().set_value("on ");
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
let config: HgBranchConfig = HgBranchConfig::try_load(module.config);
|
||||
|
||||
// TODO: Once error handling is implemented, warn the user if their config
|
||||
// truncation length is nonsensical
|
||||
@ -46,10 +42,30 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
truncated_graphemes
|
||||
};
|
||||
|
||||
module.create_segment(
|
||||
"name",
|
||||
&config.branch_name.with_value(&truncated_and_symbol),
|
||||
);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"branch" => Some(Ok(truncated_and_symbol.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `hg_branch`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module, SegmentConfig};
|
||||
use super::{Context, Module};
|
||||
use std::ffi::OsString;
|
||||
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::hostname::HostnameConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the system hostname
|
||||
///
|
||||
@ -42,10 +43,26 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
host.as_ref()
|
||||
};
|
||||
|
||||
module.set_style(config.style);
|
||||
let hostname_stacked = format!("{}{}{}", config.prefix, host, config.suffix);
|
||||
module.create_segment("hostname", &SegmentConfig::new(&hostname_stacked));
|
||||
module.get_prefix().set_value("on ");
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"hostname" => Some(Ok(host)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `hostname`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::configs::java::JavaConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::modules::utils::java_version_parser;
|
||||
use crate::utils;
|
||||
@ -25,12 +26,31 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
Some(java_version) => {
|
||||
let mut module = context.new_module("java");
|
||||
let config: JavaConfig = JavaConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
let formatted_version = format_java_version(java_version)?;
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(&formatted_version)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `java`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
Some(module)
|
||||
}
|
||||
None => None,
|
||||
|
@ -1,14 +1,12 @@
|
||||
use super::{Context, Module};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::config::{RootModuleConfig, SegmentConfig};
|
||||
use crate::configs::jobs::JobsConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a segment to show if there are any active jobs running
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("jobs");
|
||||
let config: JobsConfig = JobsConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
let config = JobsConfig::try_load(module.config);
|
||||
|
||||
let props = &context.properties;
|
||||
let num_of_jobs = props
|
||||
@ -20,11 +18,37 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
if num_of_jobs == 0 {
|
||||
return None;
|
||||
}
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
if num_of_jobs > config.threshold {
|
||||
module.create_segment("number", &SegmentConfig::new(&num_of_jobs.to_string()));
|
||||
}
|
||||
module.get_prefix().set_value("");
|
||||
|
||||
let module_number = if num_of_jobs > config.threshold {
|
||||
num_of_jobs.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"number" => Some(Ok(module_number.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `jobs`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::julia::JuliaConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Julia version
|
||||
@ -21,14 +22,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let mut module = context.new_module("julia");
|
||||
let config: JuliaConfig = JuliaConfig::try_load(module.config);
|
||||
let config = JuliaConfig::try_load(module.config);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => {
|
||||
format_julia_version(&utils::exec_cmd("julia", &["--version"])?.stdout.as_str())
|
||||
.map(Ok)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
|
||||
let formatted_version =
|
||||
format_julia_version(&utils::exec_cmd("julia", &["--version"])?.stdout.as_str())?;
|
||||
module.create_segment("version", &config.version.with_value(&formatted_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `julia`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -6,10 +6,9 @@ use std::path;
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::kubernetes::KubernetesConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
const KUBERNETES_PREFIX: &str = "on ";
|
||||
|
||||
fn get_kube_context(contents: &str) -> Option<(String, String)> {
|
||||
let yaml_docs = YamlLoader::load_from_str(&contents).ok()?;
|
||||
if yaml_docs.is_empty() {
|
||||
@ -63,23 +62,44 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
};
|
||||
|
||||
module.set_style(config.style);
|
||||
module.get_prefix().set_value(KUBERNETES_PREFIX);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"context" => match config.context_aliases.get(&kube_ctx) {
|
||||
None => Some(Ok(kube_ctx.as_str())),
|
||||
Some(&alias) => Some(Ok(alias)),
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"namespace" => {
|
||||
if kube_ns != "" {
|
||||
Some(Ok(kube_ns.as_str()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `kubernetes`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
let displayed_context = match config.context_aliases.get(&kube_ctx) {
|
||||
None => &kube_ctx,
|
||||
Some(&alias) => alias,
|
||||
};
|
||||
|
||||
module.create_segment("context", &config.context.with_value(&displayed_context));
|
||||
if kube_ns != "" {
|
||||
module.create_segment(
|
||||
"namespace",
|
||||
&config.namespace.with_value(&format!(" ({})", kube_ns)),
|
||||
);
|
||||
}
|
||||
Some(module)
|
||||
}
|
||||
None => None,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{Context, Module};
|
||||
use crate::config::SegmentConfig;
|
||||
use crate::segment::Segment;
|
||||
|
||||
/// Creates a module for the line break
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
@ -7,10 +7,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
let mut module = context.new_module("line_break");
|
||||
|
||||
module.get_prefix().set_value("");
|
||||
module.get_suffix().set_value("");
|
||||
|
||||
module.create_segment("character", &SegmentConfig::new(LINE_ENDING));
|
||||
module.set_segments(vec![Segment {
|
||||
_name: "line_break".to_string(),
|
||||
style: None,
|
||||
value: LINE_ENDING.to_string(),
|
||||
}]);
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use sysinfo::{RefreshKind, SystemExt};
|
||||
use super::{Context, Module, RootModuleConfig, Shell};
|
||||
|
||||
use crate::configs::memory_usage::MemoryConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
fn format_kib(n_kib: u64) -> String {
|
||||
let byte = Byte::from_unit(n_kib as f64, ByteUnit::KiB).unwrap_or_else(|_| Byte::from_bytes(0));
|
||||
@ -12,6 +13,14 @@ fn format_kib(n_kib: u64) -> String {
|
||||
display_bytes
|
||||
}
|
||||
|
||||
fn format_pct(pct_number: f64, pct_sign: &str) -> String {
|
||||
format!("{:.0}{}", pct_number, pct_sign)
|
||||
}
|
||||
|
||||
fn format_usage_total(usage: u64, total: u64) -> String {
|
||||
format!("{}/{}", format_kib(usage), format_kib(total))
|
||||
}
|
||||
|
||||
/// Creates a module with system memory usage information
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("memory_usage");
|
||||
@ -19,7 +28,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
// TODO: Update when v1.0 printing refactor is implemented to only
|
||||
// print escapes in a prompt context.
|
||||
let percent_sign = match context.shell {
|
||||
let pct_sign = match context.shell {
|
||||
Shell::Zsh => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc`
|
||||
_ => "%",
|
||||
};
|
||||
@ -28,54 +37,52 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
|
||||
let system = sysinfo::System::new_with_specifics(RefreshKind::new().with_memory());
|
||||
|
||||
let used_memory_kib = system.get_used_memory();
|
||||
let total_memory_kib = system.get_total_memory();
|
||||
|
||||
let percent_mem_used = (used_memory_kib as f64 / total_memory_kib as f64) * 100.;
|
||||
let ram_used = (used_memory_kib as f64 / total_memory_kib as f64) * 100.;
|
||||
let ram_pct = format_pct(ram_used, pct_sign);
|
||||
|
||||
let threshold = config.threshold;
|
||||
|
||||
if percent_mem_used.round() < threshold as f64 {
|
||||
if ram_used.round() < threshold as f64 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let show_percentage = config.show_percentage;
|
||||
|
||||
let ram = if show_percentage {
|
||||
format!("{:.0}{}", percent_mem_used, percent_sign)
|
||||
} else {
|
||||
format!(
|
||||
"{}/{}",
|
||||
format_kib(used_memory_kib),
|
||||
format_kib(total_memory_kib)
|
||||
)
|
||||
};
|
||||
module.create_segment("ram", &config.ram.with_value(&ram));
|
||||
|
||||
// swap only shown if enabled and there is swap on the system
|
||||
let ram = format_usage_total(used_memory_kib, total_memory_kib);
|
||||
let total_swap_kib = system.get_total_swap();
|
||||
if config.show_swap && total_swap_kib > 0 {
|
||||
let used_swap_kib = system.get_used_swap();
|
||||
let percent_swap_used = (used_swap_kib as f64 / total_swap_kib as f64) * 100.;
|
||||
let used_swap_kib = system.get_used_swap();
|
||||
let percent_swap_used = (used_swap_kib as f64 / total_swap_kib as f64) * 100.;
|
||||
let swap_pct = format_pct(percent_swap_used, pct_sign);
|
||||
let swap = format_usage_total(used_swap_kib, total_swap_kib);
|
||||
|
||||
let swap = if show_percentage {
|
||||
format!("{:.0}{}", percent_swap_used, percent_sign)
|
||||
} else {
|
||||
format!(
|
||||
"{}/{}",
|
||||
format_kib(used_swap_kib),
|
||||
format_kib(total_swap_kib)
|
||||
)
|
||||
};
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"ram" => Some(Ok(&ram)),
|
||||
"ram_pct" => Some(Ok(&ram_pct)),
|
||||
// swap only shown if there is swap on the system
|
||||
"swap" if total_swap_kib > 0 => Some(Ok(&swap)),
|
||||
"swap_pct" if total_swap_kib > 0 => Some(Ok(&swap_pct)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("separator", &config.separator);
|
||||
module.create_segment("swap", &config.swap.with_value(&swap));
|
||||
}
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `memory_usage`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ mod zig;
|
||||
#[cfg(feature = "battery")]
|
||||
mod battery;
|
||||
|
||||
use crate::config::{RootModuleConfig, SegmentConfig};
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::context::{Context, Shell};
|
||||
use crate::module::Module;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::nim::NimConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Nim version
|
||||
@ -19,16 +20,38 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let nim_version_output = utils::exec_cmd("nim", &["--version"])?.stdout;
|
||||
let formatted_nim_version = format!("v{}", parse_nim_version(&nim_version_output)?);
|
||||
|
||||
let mut module = context.new_module("nim");
|
||||
let config = NimConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => utils::exec_cmd("nim", &["--version"])
|
||||
.map(|command_output| command_output.stdout)
|
||||
.and_then(|nim_version_output| {
|
||||
Some(format!("v{}", parse_nim_version(&nim_version_output)?))
|
||||
})
|
||||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_nim_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `nim`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::nix_shell::NixShellConfig;
|
||||
|
||||
// IN_NIX_SHELL should be "pure" or "impure" but lorri uses "1" for "impure"
|
||||
// https://github.com/target/lorri/issues/140
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module showing if inside a nix-shell
|
||||
///
|
||||
@ -13,43 +11,53 @@ use crate::configs::nix_shell::NixShellConfig;
|
||||
/// determine if it's inside a nix-shell and the name of it.
|
||||
///
|
||||
/// The following options are availables:
|
||||
/// - use_name (bool) // print the name of the nix-shell
|
||||
/// - impure_msg (string) // change the impure msg
|
||||
/// - pure_msg (string) // change the pure msg
|
||||
///
|
||||
/// Will display the following:
|
||||
/// - name (pure) // use_name == true in a pure nix-shell
|
||||
/// - name (impure) // use_name == true in an impure nix-shell
|
||||
/// - pure // use_name == false in a pure nix-shell
|
||||
/// - impure // use_name == false in an impure nix-shell
|
||||
/// - pure (name) // $name == "name" in a pure nix-shell
|
||||
/// - impure (name) // $name == "name" in an impure nix-shell
|
||||
/// - pure // $name == "" in a pure nix-shell
|
||||
/// - impure // $name == "" in an impure nix-shell
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("nix_shell");
|
||||
let config: NixShellConfig = NixShellConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
|
||||
let shell_name = env::var("name").ok();
|
||||
let shell_type = env::var("IN_NIX_SHELL").ok()?;
|
||||
let shell_type_segment: SegmentConfig = match shell_type.as_ref() {
|
||||
"1" | "impure" => config.impure_msg,
|
||||
let shell_type_format = match shell_type.as_ref() {
|
||||
"impure" => config.impure_msg,
|
||||
"pure" => config.pure_msg,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if config.use_name {
|
||||
if let Ok(name) = env::var("name") {
|
||||
module.create_segment(
|
||||
"nix_shell",
|
||||
&shell_type_segment.with_value(&format!("{} ({})", name, shell_type_segment.value)),
|
||||
);
|
||||
} else {
|
||||
module.create_segment("nix_shell", &shell_type_segment);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
"state" => Some(shell_type_format),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"name" => shell_name.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `nix_shell`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
module.create_segment("nix_shell", &shell_type_segment);
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::nodejs::NodejsConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Node.js version
|
||||
@ -27,16 +28,32 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let node_version = utils::exec_cmd("node", &["--version"])?.stdout;
|
||||
|
||||
let mut module = context.new_module("nodejs");
|
||||
let config: NodejsConfig = NodejsConfig::try_load(module.config);
|
||||
let config = NodejsConfig::try_load(module.config);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(utils::exec_cmd("node", &["--version"])?.stdout)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_style(config.style);
|
||||
|
||||
let formatted_version = node_version.trim();
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(formatted_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `nodejs`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::ocaml::OCamlConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current OCaml version
|
||||
@ -35,14 +36,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
utils::exec_cmd("ocaml", &["-vnum"])?.stdout
|
||||
};
|
||||
|
||||
let formatted_version = format!("v{}", &ocaml_version.trim());
|
||||
|
||||
let mut module = context.new_module("ocaml");
|
||||
let config = OCamlConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
let config: OCamlConfig = OCamlConfig::try_load(module.config);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(format!("v{}", &ocaml_version))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `ocaml`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,33 +1,47 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Context, Module};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
use crate::configs::package::PackageConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
use regex::Regex;
|
||||
use serde_json as json;
|
||||
|
||||
use super::{RootModuleConfig, SegmentConfig};
|
||||
use crate::configs::package::PackageConfig;
|
||||
|
||||
/// Creates a module with the current package version
|
||||
///
|
||||
/// Will display if a version is defined for your Node.js or Rust project (if one exists)
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("package");
|
||||
let config: PackageConfig = PackageConfig::try_load(module.config);
|
||||
let module_version = get_package_version(&context.current_dir, &config)?;
|
||||
|
||||
match get_package_version(&context.current_dir, &config) {
|
||||
Some(package_version) => {
|
||||
module.set_style(config.style);
|
||||
module.get_prefix().set_value("is ");
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(&module_version)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&package_version));
|
||||
|
||||
Some(module)
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `package`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn extract_cargo_version(file_contents: &str) -> Option<String> {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::php::PhpConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current PHP version
|
||||
@ -27,16 +28,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
],
|
||||
) {
|
||||
Some(php_cmd_output) => {
|
||||
let php_version = php_cmd_output.stdout;
|
||||
|
||||
let mut module = context.new_module("php");
|
||||
let config: PhpConfig = PhpConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => format_php_version(&php_cmd_output.stdout).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
let formatted_version = format_php_version(&php_version)?;
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `php`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::purescript::PureScriptConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current PureScript version
|
||||
@ -20,14 +21,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let purs_version = utils::exec_cmd("purs", &["--version"])?.stdout;
|
||||
let formatted_version = Some(format!("v{}", purs_version.trim()))?;
|
||||
|
||||
let mut module = context.new_module("purescript");
|
||||
let config: PureScriptConfig = PureScriptConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(format!("v{}", purs_version.trim()))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `purescript`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
use crate::configs::python::PythonConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Python version
|
||||
@ -41,25 +42,39 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
|
||||
if config.pyenv_version_name {
|
||||
let python_version = utils::exec_cmd("pyenv", &["version-name"])?.stdout;
|
||||
module.create_segment("pyenv_prefix", &config.pyenv_prefix);
|
||||
module.create_segment("version", &SegmentConfig::new(&python_version.trim()));
|
||||
let python_version = if config.pyenv_version_name {
|
||||
utils::exec_cmd("pyenv", &["version-name"])?.stdout
|
||||
} else {
|
||||
let python_version = get_python_version(&config.python_binary)?;
|
||||
let formatted_version = format_python_version(&python_version);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
let version = get_python_version(&config.python_binary)?;
|
||||
format_python_version(&version)
|
||||
};
|
||||
let virtual_env = get_python_virtual_env();
|
||||
|
||||
if let Some(virtual_env) = get_python_virtual_env() {
|
||||
module.create_segment(
|
||||
"virtualenv",
|
||||
&SegmentConfig::new(&format!(" ({})", virtual_env)),
|
||||
);
|
||||
};
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(python_version.trim())),
|
||||
"virtualenv" => virtual_env.as_ref().map(|e| Ok(e.trim())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `python`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::ruby::RubyConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Ruby version
|
||||
@ -19,15 +20,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ruby_version = utils::exec_cmd("ruby", &["-v"])?.stdout;
|
||||
let formatted_version = format_ruby_version(&ruby_version)?;
|
||||
|
||||
let mut module = context.new_module("ruby");
|
||||
let config: RubyConfig = RubyConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
let config = RubyConfig::try_load(module.config);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => {
|
||||
format_ruby_version(&utils::exec_cmd("ruby", &["-v"])?.stdout.as_str()).map(Ok)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &SegmentConfig::new(&formatted_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `ruby`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use std::{env, fs};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::rust::RustConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the current Rust version
|
||||
///
|
||||
@ -22,6 +23,39 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut module = context.new_module("rust");
|
||||
let config = RustConfig::try_load(module.config);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|var, _| match var {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
// This may result in multiple calls to `get_module_version` when a user have
|
||||
// multiple `$version` variables defined in `format`.
|
||||
"version" => get_module_version(context).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `rust`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_module_version(context: &Context) -> Option<String> {
|
||||
// `$CARGO_HOME/bin/rustc(.exe) --version` may attempt installing a rustup toolchain.
|
||||
// https://github.com/starship/starship/issues/417
|
||||
//
|
||||
@ -56,14 +90,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
format_rustc_version(execute_rustc_version()?)
|
||||
};
|
||||
|
||||
let mut module = context.new_module("rust");
|
||||
let config = RustConfig::try_load(module.config);
|
||||
module.set_style(config.style);
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &config.version.with_value(&module_version));
|
||||
|
||||
Some(module)
|
||||
Some(module_version)
|
||||
}
|
||||
|
||||
fn env_rustup_toolchain() -> Option<String> {
|
||||
|
@ -1,28 +1,44 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::config::RootModuleConfig;
|
||||
use crate::configs::singularity::SingularityConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the current Singularity image
|
||||
///
|
||||
/// Will display the Singularity image if `$SINGULARITY_NAME` is set.
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let singularity_env = env::var("SINGULARITY_NAME").unwrap_or_else(|_| "".into());
|
||||
if singularity_env.trim().is_empty() {
|
||||
return None;
|
||||
}
|
||||
let singularity_env = env::var("SINGULARITY_NAME").ok();
|
||||
singularity_env.as_ref()?;
|
||||
|
||||
let mut module = context.new_module("singularity");
|
||||
let config = SingularityConfig::try_load(module.config);
|
||||
let config: SingularityConfig = SingularityConfig::try_load(module.config);
|
||||
|
||||
module.get_prefix().set_value(config.label);
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"env" => singularity_env.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
let env_var_stacked = format!("{}{}{}", config.prefix, singularity_env, config.suffix);
|
||||
module.create_segment("singularity", &SegmentConfig::new(&env_var_stacked));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `singularity`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::terraform::TerraformConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
@ -25,20 +27,34 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("terraform");
|
||||
let config: TerraformConfig = TerraformConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => format_terraform_version(
|
||||
&utils::exec_cmd("terraform", &["version"])?.stdout.as_str(),
|
||||
)
|
||||
.map(Ok),
|
||||
"workspace" => get_terraform_workspace(&context.current_dir).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
if config.show_version {
|
||||
let terraform_version =
|
||||
format_terraform_version(&utils::exec_cmd("terraform", &["version"])?.stdout.as_str())?;
|
||||
module.create_segment("version", &config.version.with_value(&terraform_version));
|
||||
}
|
||||
|
||||
let terraform_workspace = &get_terraform_workspace(&context.current_dir)?;
|
||||
module.create_segment(
|
||||
"workspace",
|
||||
&config.workspace.with_value(&terraform_workspace),
|
||||
);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `terraform`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
use chrono::{DateTime, FixedOffset, Local, NaiveTime, Utc};
|
||||
|
||||
use super::{Context, Module};
|
||||
|
||||
use crate::config::{RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
use crate::configs::time::TimeConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Outputs the current time
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
const TIME_PREFIX: &str = "at ";
|
||||
|
||||
let mut module = context.new_module("time");
|
||||
let config: TimeConfig = TimeConfig::try_load(module.config);
|
||||
if config.disabled {
|
||||
@ -23,7 +20,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
|
||||
let default_format = if config.use_12hr { "%r" } else { "%T" };
|
||||
let time_format = config.format.unwrap_or(default_format);
|
||||
let time_format = config.time_format.unwrap_or(default_format);
|
||||
|
||||
log::trace!(
|
||||
"Timer module is enabled with format string: {}",
|
||||
@ -44,17 +41,26 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
format_time(&time_format, Local::now())
|
||||
};
|
||||
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"time" => Some(Ok(&formatted_time_string)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.get_prefix().set_value(TIME_PREFIX);
|
||||
|
||||
module.create_segment(
|
||||
"time",
|
||||
&SegmentConfig {
|
||||
value: &formatted_time_string,
|
||||
style: None,
|
||||
},
|
||||
);
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `time`: \n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::env;
|
||||
|
||||
use super::{Context, Module, RootModuleConfig, SegmentConfig};
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::username::UsernameConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current user's username
|
||||
@ -23,13 +24,32 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let config: UsernameConfig = UsernameConfig::try_load(module.config);
|
||||
|
||||
if user != logname || ssh_connection.is_some() || user_uid == ROOT_UID || config.show_always {
|
||||
let module_style = match user_uid {
|
||||
Some(0) => config.style_root,
|
||||
_ => config.style_user,
|
||||
};
|
||||
|
||||
module.set_style(module_style);
|
||||
module.create_segment("username", &SegmentConfig::new(&user?));
|
||||
let username = user?;
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
"style" => {
|
||||
let module_style = match user_uid {
|
||||
Some(0) => config.style_root,
|
||||
_ => config.style_user,
|
||||
};
|
||||
Some(Ok(module_style))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"user" => Some(Ok(&username)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `username`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
} else {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::{Context, Module, RootModuleConfig};
|
||||
|
||||
use crate::configs::zig::ZigConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::utils;
|
||||
|
||||
/// Creates a module with the current Zig version
|
||||
@ -26,10 +27,30 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("zig");
|
||||
let config = ZigConfig::try_load(module.config);
|
||||
|
||||
module.set_style(config.style);
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_meta(|variable, _| match variable {
|
||||
"symbol" => Some(config.symbol),
|
||||
_ => None,
|
||||
})
|
||||
.map_style(|variable| match variable {
|
||||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"version" => Some(Ok(zig_version.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
});
|
||||
|
||||
module.create_segment("symbol", &config.symbol);
|
||||
module.create_segment("version", &config.version.with_value(&zig_version));
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `zig`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
204
src/print.rs
204
src/print.rs
@ -1,14 +1,18 @@
|
||||
use ansi_term::ANSIStrings;
|
||||
use clap::ArgMatches;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::{self, Debug, Write as FmtWrite};
|
||||
use std::io::{self, Write};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::configs::PROMPT_ORDER;
|
||||
use crate::context::{Context, Shell};
|
||||
use crate::formatter::{StringFormatter, VariableHolder};
|
||||
use crate::module::Module;
|
||||
use crate::module::ALL_MODULES;
|
||||
use crate::modules;
|
||||
use crate::segment::Segment;
|
||||
|
||||
pub fn prompt(args: ArgMatches) {
|
||||
let context = Context::new(args);
|
||||
@ -21,34 +25,53 @@ pub fn get_prompt(context: Context) -> String {
|
||||
let config = context.config.get_root_config();
|
||||
let mut buf = String::new();
|
||||
|
||||
// Write a new line before the prompt
|
||||
if config.add_newline {
|
||||
writeln!(buf).unwrap();
|
||||
}
|
||||
|
||||
// A workaround for a fish bug (see #739,#279). Applying it to all shells
|
||||
// breaks things (see #808,#824,#834). Should only be printed in fish.
|
||||
if let Shell::Fish = context.shell {
|
||||
buf.push_str("\x1b[J"); // An ASCII control code to clear screen
|
||||
}
|
||||
|
||||
let modules = compute_modules(&context);
|
||||
|
||||
let mut print_without_prefix = true;
|
||||
let printable = modules.iter();
|
||||
|
||||
for module in printable {
|
||||
// Skip printing the prefix of a module after the line_break
|
||||
if print_without_prefix {
|
||||
let module_without_prefix = module.to_string_without_prefix(context.shell);
|
||||
write!(buf, "{}", module_without_prefix).unwrap()
|
||||
let formatter = if let Ok(formatter) = StringFormatter::new(config.format) {
|
||||
formatter
|
||||
} else {
|
||||
log::error!("Error parsing `format`");
|
||||
buf.push_str(">");
|
||||
return buf;
|
||||
};
|
||||
let modules = formatter.get_variables();
|
||||
let formatter = formatter.map_variables_to_segments(|module| {
|
||||
// Make $all display all modules
|
||||
if module == "all" {
|
||||
Some(Ok(PROMPT_ORDER
|
||||
.par_iter()
|
||||
.flat_map(|module| {
|
||||
handle_module(module, &context, &modules)
|
||||
.into_iter()
|
||||
.flat_map(|module| module.segments)
|
||||
.collect::<Vec<Segment>>()
|
||||
})
|
||||
.collect::<Vec<_>>()))
|
||||
} else if context.is_module_disabled_in_config(&module) {
|
||||
None
|
||||
} else {
|
||||
let module = module.ansi_strings_for_shell(context.shell);
|
||||
write!(buf, "{}", ANSIStrings(&module)).unwrap();
|
||||
// Get segments from module
|
||||
Some(Ok(handle_module(module, &context, &modules)
|
||||
.into_iter()
|
||||
.flat_map(|module| module.segments)
|
||||
.collect::<Vec<Segment>>()))
|
||||
}
|
||||
});
|
||||
|
||||
print_without_prefix = module.get_name() == "line_break"
|
||||
}
|
||||
// Creates a root module and prints it.
|
||||
let mut root_module = Module::new("Starship Root", "The root module", None);
|
||||
root_module.set_segments(
|
||||
formatter
|
||||
.parse(None)
|
||||
.expect("Unexpected error returned in root format variables"),
|
||||
);
|
||||
|
||||
let module_strings = root_module.ansi_strings_for_shell(context.shell);
|
||||
write!(buf, "{}", ANSIStrings(&module_strings)).unwrap();
|
||||
|
||||
buf
|
||||
}
|
||||
@ -72,16 +95,15 @@ pub fn explain(args: ArgMatches) {
|
||||
desc: String,
|
||||
}
|
||||
|
||||
let dont_print = vec!["line_break", "character"];
|
||||
let dont_print = vec!["line_break"];
|
||||
|
||||
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();
|
||||
let value = module.get_segments().join("");
|
||||
ModuleInfo {
|
||||
value: ansi_term::ANSIStrings(&ansi_strings[1..ansi_strings.len() - 1]).to_string(),
|
||||
value: ansi_term::ANSIStrings(&module.ansi_strings()).to_string(),
|
||||
value_len: value.chars().count() + count_wide_chars(&value),
|
||||
desc: module.get_description().to_owned(),
|
||||
}
|
||||
@ -132,11 +154,38 @@ pub fn explain(args: ArgMatches) {
|
||||
}
|
||||
|
||||
fn compute_modules<'a>(context: &'a Context) -> Vec<Module<'a>> {
|
||||
enum Mod<'a> {
|
||||
Builtin(&'a str),
|
||||
Custom(&'a str),
|
||||
let mut prompt_order: Vec<Module<'a>> = Vec::new();
|
||||
|
||||
let config = context.config.get_root_config();
|
||||
let formatter = if let Ok(formatter) = StringFormatter::new(config.format) {
|
||||
formatter
|
||||
} else {
|
||||
log::error!("Error parsing `format`");
|
||||
return Vec::new();
|
||||
};
|
||||
let modules = formatter.get_variables();
|
||||
|
||||
for module in &modules {
|
||||
// Manually add all modules if `$all` is encountered
|
||||
if module == "all" {
|
||||
for module in PROMPT_ORDER.iter() {
|
||||
let modules = handle_module(module, &context, &modules);
|
||||
prompt_order.extend(modules.into_iter());
|
||||
}
|
||||
} else {
|
||||
let modules = handle_module(module, &context, &modules);
|
||||
prompt_order.extend(modules.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
prompt_order
|
||||
}
|
||||
|
||||
fn handle_module<'a>(
|
||||
module: &str,
|
||||
context: &'a Context,
|
||||
module_list: &BTreeSet<String>,
|
||||
) -> Vec<Module<'a>> {
|
||||
struct DebugCustomModules<'tmp>(&'tmp toml::value::Table);
|
||||
|
||||
impl Debug for DebugCustomModules<'_> {
|
||||
@ -145,74 +194,63 @@ fn compute_modules<'a>(context: &'a Context) -> Vec<Module<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut prompt_order: Vec<Mod> = Vec::new();
|
||||
let mut modules: Vec<Option<Module>> = Vec::new();
|
||||
|
||||
// Write out a custom prompt order
|
||||
let config_prompt_order = context.config.get_root_config().prompt_order;
|
||||
|
||||
for module in &config_prompt_order {
|
||||
if ALL_MODULES.contains(module) {
|
||||
// Write out a module if it isn't disabled
|
||||
if !context.is_module_disabled_in_config(*module) {
|
||||
prompt_order.push(Mod::Builtin(module));
|
||||
}
|
||||
} else if *module == "custom" {
|
||||
// Write out all custom modules, except for those that are explicitly set
|
||||
if let Some(custom_modules) = context.config.get_custom_modules() {
|
||||
for (custom_module, config) in custom_modules {
|
||||
if should_add_implicit_custom_module(
|
||||
custom_module,
|
||||
config,
|
||||
&config_prompt_order,
|
||||
) {
|
||||
prompt_order.push(Mod::Custom(custom_module));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if module.starts_with("custom.") {
|
||||
// Write out a custom module if it isn't disabled (and it exists...)
|
||||
match context.is_custom_module_disabled_in_config(&module[7..]) {
|
||||
Some(true) => (), // Module is disabled, we don't add it to the prompt
|
||||
Some(false) => prompt_order.push(Mod::Custom(&module[7..])),
|
||||
None => match context.config.get_custom_modules() {
|
||||
Some(modules) => log::debug!(
|
||||
"prompt_order contains custom module \"{}\", but no configuration was provided. Configuration for the following modules were provided: {:?}",
|
||||
module,
|
||||
DebugCustomModules(modules),
|
||||
),
|
||||
None => log::debug!(
|
||||
"prompt_order contains custom module \"{}\", but no configuration was provided.",
|
||||
module,
|
||||
),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
log::debug!(
|
||||
"Expected prompt_order to contain value from {:?}. Instead received {}",
|
||||
ALL_MODULES,
|
||||
module,
|
||||
);
|
||||
if ALL_MODULES.contains(&module) {
|
||||
// Write out a module if it isn't disabled
|
||||
if !context.is_module_disabled_in_config(module) {
|
||||
modules.push(modules::handle(module, &context));
|
||||
}
|
||||
} else if module == "custom" {
|
||||
// Write out all custom modules, except for those that are explicitly set
|
||||
if let Some(custom_modules) = context.config.get_custom_modules() {
|
||||
let custom_modules = custom_modules
|
||||
.iter()
|
||||
.map(|(custom_module, config)| {
|
||||
if should_add_implicit_custom_module(custom_module, config, &module_list) {
|
||||
modules::custom::module(custom_module, &context)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Option<Module<'a>>>>();
|
||||
modules.extend(custom_modules)
|
||||
}
|
||||
} else if module.starts_with("custom.") {
|
||||
// Write out a custom module if it isn't disabled (and it exists...)
|
||||
match context.is_custom_module_disabled_in_config(&module[7..]) {
|
||||
Some(true) => (), // Module is disabled, we don't add it to the prompt
|
||||
Some(false) => modules.push(modules::custom::module(&module[7..], &context)),
|
||||
None => match context.config.get_custom_modules() {
|
||||
Some(modules) => log::debug!(
|
||||
"prompt_order contains custom module \"{}\", but no configuration was provided. Configuration for the following modules were provided: {:?}",
|
||||
module,
|
||||
DebugCustomModules(modules),
|
||||
),
|
||||
None => log::debug!(
|
||||
"prompt_order contains custom module \"{}\", but no configuration was provided.",
|
||||
module,
|
||||
),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
log::debug!(
|
||||
"Expected prompt_order to contain value from {:?}. Instead received {}",
|
||||
ALL_MODULES,
|
||||
module,
|
||||
);
|
||||
}
|
||||
|
||||
prompt_order
|
||||
.par_iter()
|
||||
.map(|module| match module {
|
||||
Mod::Builtin(builtin) => modules::handle(builtin, context),
|
||||
Mod::Custom(custom) => modules::custom::module(custom, context),
|
||||
}) // Compute segments
|
||||
.flatten() // Remove segments set to `None`
|
||||
.collect::<Vec<Module<'a>>>()
|
||||
modules.into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
fn should_add_implicit_custom_module(
|
||||
custom_module: &str,
|
||||
config: &toml::Value,
|
||||
config_prompt_order: &[&str],
|
||||
module_list: &BTreeSet<String>,
|
||||
) -> bool {
|
||||
let is_explicitly_specified = config_prompt_order.iter().any(|x| {
|
||||
x.len() == 7 + custom_module.len() && &x[..7] == "custom." && &x[7..] == custom_module
|
||||
});
|
||||
let explicit_module_name = format!("custom.{}", custom_module);
|
||||
let is_explicitly_specified = module_list.contains(&explicit_module_name);
|
||||
|
||||
if is_explicitly_specified {
|
||||
// The module is already specified explicitly, so we skip it
|
||||
|
@ -22,7 +22,7 @@ fn region_set() -> io::Result<()> {
|
||||
let output = common::render_module("aws")
|
||||
.env("AWS_REGION", "ap-northeast-2")
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ ap-northeast-2"));
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-2)"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@ -37,7 +37,7 @@ fn region_set_with_alias() -> io::Result<()> {
|
||||
ap-southeast-2 = "au"
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ au"));
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (au)"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@ -49,7 +49,7 @@ fn default_region_set() -> io::Result<()> {
|
||||
.env("AWS_REGION", "ap-northeast-2")
|
||||
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ ap-northeast-1"));
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-1)"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@ -112,7 +112,7 @@ region = us-east-2
|
||||
let output = common::render_module("aws")
|
||||
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ us-east-1"));
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (us-east-1)"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
@ -155,10 +155,6 @@ fn profile_and_region_set_with_display_all() -> io::Result<()> {
|
||||
let output = common::render_module("aws")
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.env("AWS_REGION", "ap-northeast-1")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "all"
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!(
|
||||
"on {} ",
|
||||
@ -173,10 +169,6 @@ fn profile_and_region_set_with_display_all() -> io::Result<()> {
|
||||
fn profile_set_with_display_all() -> io::Result<()> {
|
||||
let output = common::render_module("aws")
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "all"
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
@ -188,12 +180,8 @@ fn profile_set_with_display_all() -> io::Result<()> {
|
||||
fn region_set_with_display_all() -> io::Result<()> {
|
||||
let output = common::render_module("aws")
|
||||
.env("AWS_REGION", "ap-northeast-1")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "all"
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ ap-northeast-1"));
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ (ap-northeast-1)"));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@ -206,7 +194,7 @@ fn profile_and_region_set_with_display_region() -> io::Result<()> {
|
||||
.env("AWS_DEFAULT_REGION", "ap-northeast-1")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "region"
|
||||
format = "on [$symbol$region]($style) "
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ ap-northeast-1"));
|
||||
@ -222,7 +210,7 @@ fn profile_and_region_set_with_display_profile() -> io::Result<()> {
|
||||
.env("AWS_REGION", "ap-northeast-1")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "profile"
|
||||
format = "on [$symbol$profile]($style) "
|
||||
})
|
||||
.output()?;
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ astronauts"));
|
||||
@ -237,10 +225,10 @@ fn region_set_with_display_profile() -> io::Result<()> {
|
||||
.env("AWS_REGION", "ap-northeast-1")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "profile"
|
||||
format = "on [$symbol$profile]($style) "
|
||||
})
|
||||
.output()?;
|
||||
let expected = "";
|
||||
let expected = format!("on {} ", Color::Yellow.bold().paint("☁️ "));
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
Ok(())
|
||||
@ -252,7 +240,7 @@ fn region_not_set_with_display_region() -> io::Result<()> {
|
||||
let output = common::render_module("aws")
|
||||
.use_config(toml::toml! {
|
||||
[aws]
|
||||
displayed_items = "region"
|
||||
format = "on [$symbol$region]($style) "
|
||||
})
|
||||
.output()?;
|
||||
let expected = "";
|
||||
|
@ -4,7 +4,7 @@ use std::io;
|
||||
use crate::common::{self, TestCommand};
|
||||
|
||||
#[test]
|
||||
fn char_module_success_status() -> io::Result<()> {
|
||||
fn success_status() -> io::Result<()> {
|
||||
let expected = format!("{} ", Color::Green.bold().paint("❯"));
|
||||
|
||||
// Status code 0
|
||||
@ -23,7 +23,7 @@ fn char_module_success_status() -> io::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_module_failure_status() -> io::Result<()> {
|
||||
fn failure_status() -> io::Result<()> {
|
||||
let expected = format!("{} ", Color::Red.bold().paint("❯"));
|
||||
|
||||
let exit_values = ["1", "54321", "-5000"];
|
||||
@ -39,9 +39,9 @@ fn char_module_failure_status() -> io::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_module_symbolyes_status() -> io::Result<()> {
|
||||
fn custom_symbol() -> io::Result<()> {
|
||||
let expected_fail = format!("{} ", Color::Red.bold().paint("✖"));
|
||||
let expected_success = format!("{} ", Color::Green.bold().paint("❯"));
|
||||
let expected_success = format!("{} ", Color::Green.bold().paint("➜"));
|
||||
|
||||
let exit_values = ["1", "54321", "-5000"];
|
||||
|
||||
@ -51,7 +51,9 @@ fn char_module_symbolyes_status() -> io::Result<()> {
|
||||
let output = common::render_module("character")
|
||||
.use_config(toml::toml! {
|
||||
[character]
|
||||
use_symbol_for_status = true
|
||||
success_symbol = "[➜](bold green)"
|
||||
error_symbol = "[✖](bold red)"
|
||||
|
||||
})
|
||||
.arg(arg)
|
||||
.output()?;
|
||||
@ -63,7 +65,8 @@ fn char_module_symbolyes_status() -> io::Result<()> {
|
||||
let output = common::render_module("character")
|
||||
.use_config(toml::toml! {
|
||||
[character]
|
||||
use_symbol_for_status = true
|
||||
success_symbol = "[➜](bold green)"
|
||||
error_symbol = "[✖](bold red)"
|
||||
})
|
||||
.arg("--status=0")
|
||||
.output()?;
|
||||
@ -74,11 +77,10 @@ fn char_module_symbolyes_status() -> io::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_module_zsh_keymap() -> io::Result<()> {
|
||||
let expected_vicmd = "❮";
|
||||
// TODO make this less... well, stupid when ANSI escapes can be mocked out
|
||||
let expected_specified = "I HIGHLY DOUBT THIS WILL SHOW UP IN OTHER OUTPUT";
|
||||
let expected_other = "❯";
|
||||
fn zsh_keymap() -> io::Result<()> {
|
||||
let expected_vicmd = format!("{} ", Color::Green.bold().paint("❮"));
|
||||
let expected_specified = format!("{} ", Color::Green.bold().paint("V"));
|
||||
let expected_other = format!("{} ", Color::Green.bold().paint("❯"));
|
||||
|
||||
// zle keymap is vicmd
|
||||
let output = common::render_module("character")
|
||||
@ -86,19 +88,19 @@ fn char_module_zsh_keymap() -> io::Result<()> {
|
||||
.arg("--keymap=vicmd")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_vicmd));
|
||||
assert_eq!(expected_vicmd, actual);
|
||||
|
||||
// specified vicmd character
|
||||
let output = common::render_module("character")
|
||||
.use_config(toml::toml! {
|
||||
[character]
|
||||
vicmd_symbol = "I HIGHLY DOUBT THIS WILL SHOW UP IN OTHER OUTPUT"
|
||||
vicmd_symbol = "[V](bold green)"
|
||||
})
|
||||
.env("STARSHIP_SHELL", "zsh")
|
||||
.arg("--keymap=vicmd")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_specified));
|
||||
assert_eq!(expected_specified, actual);
|
||||
|
||||
// zle keymap is other
|
||||
let output = common::render_module("character")
|
||||
@ -106,17 +108,16 @@ fn char_module_zsh_keymap() -> io::Result<()> {
|
||||
.arg("--keymap=visual")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_other));
|
||||
assert_eq!(expected_other, actual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_module_fish_keymap() -> io::Result<()> {
|
||||
let expected_vicmd = "❮";
|
||||
// TODO make this less... well, stupid when ANSI escapes can be mocked out
|
||||
let expected_specified = "I HIGHLY DOUBT THIS WILL SHOW UP IN OTHER OUTPUT";
|
||||
let expected_other = "❯";
|
||||
fn fish_keymap() -> io::Result<()> {
|
||||
let expected_vicmd = format!("{} ", Color::Green.bold().paint("❮"));
|
||||
let expected_specified = format!("{} ", Color::Green.bold().paint("V"));
|
||||
let expected_other = format!("{} ", Color::Green.bold().paint("❯"));
|
||||
|
||||
// fish keymap is default
|
||||
let output = common::render_module("character")
|
||||
@ -124,19 +125,19 @@ fn char_module_fish_keymap() -> io::Result<()> {
|
||||
.arg("--keymap=default")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_vicmd));
|
||||
assert_eq!(expected_vicmd, actual);
|
||||
|
||||
// specified vicmd character
|
||||
let output = common::render_module("character")
|
||||
.use_config(toml::toml! {
|
||||
[character]
|
||||
vicmd_symbol = "I HIGHLY DOUBT THIS WILL SHOW UP IN OTHER OUTPUT"
|
||||
vicmd_symbol = "[V](bold green)"
|
||||
})
|
||||
.env("STARSHIP_SHELL", "fish")
|
||||
.arg("--keymap=default")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_specified));
|
||||
assert_eq!(expected_specified, actual);
|
||||
|
||||
// fish keymap is other
|
||||
let output = common::render_module("character")
|
||||
@ -144,7 +145,7 @@ fn char_module_fish_keymap() -> io::Result<()> {
|
||||
.arg("--keymap=visual")
|
||||
.output()?;
|
||||
let actual = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(actual.contains(&expected_other));
|
||||
assert_eq!(expected_other, actual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ fn config_1s_duration_prefix_underwent() -> io::Result<()> {
|
||||
let output = common::render_module("cmd_duration")
|
||||
.use_config(toml::toml! {
|
||||
[cmd_duration]
|
||||
prefix = "underwent "
|
||||
format = "underwent [$duration]($style) "
|
||||
})
|
||||
.arg("--cmd-duration=1000")
|
||||
.output()?;
|
||||
@ -80,7 +80,7 @@ fn config_5s_duration_prefix_underwent() -> io::Result<()> {
|
||||
let output = common::render_module("cmd_duration")
|
||||
.use_config(toml::toml! {
|
||||
[cmd_duration]
|
||||
prefix = "underwent "
|
||||
format = "underwent [$duration]($style) "
|
||||
})
|
||||
.arg("--cmd-duration=5000")
|
||||
.output()?;
|
||||
|
@ -16,7 +16,7 @@ const EXE_PATH: &str = "./target/debug/starship.exe";
|
||||
const EXE_PATH: &str = "./target/debug/starship";
|
||||
|
||||
/// Render the full starship prompt
|
||||
pub fn render_prompt() -> process::Command {
|
||||
pub fn _render_prompt() -> process::Command {
|
||||
let mut command = process::Command::new(EXE_PATH);
|
||||
|
||||
command
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user