mirror of
https://github.com/starship/starship.git
synced 2024-11-25 01:33:41 +01:00
feat(color): add prev_fg and prev_bg as color specifiers based on the previous foreground/background colors respectively (#6017)
feat(color): add prevfg,prevbg as color specifiers based on the previous foreground/background colors respectively Co-authored-by: Vladimir Lushnikov <vladimir@solidninja.is>
This commit is contained in:
parent
e0281868c9
commit
9a3e87f2cb
@ -358,7 +358,9 @@ Style strings are a list of words, separated by whitespace. The words are not ca
|
||||
- `<color>`
|
||||
- `none`
|
||||
|
||||
where `<color>` is a color specifier (discussed below). `fg:<color>` and `<color>` currently do the same thing, though this may change in the future. `inverted` swaps the background and foreground colors. The order of words in the string does not matter.
|
||||
where `<color>` is a color specifier (discussed below). `fg:<color>` and `<color>` currently do the same thing, though this may change in the future.
|
||||
`<color>` can also be set to `prev_fg` or `prev_bg` which evaluates to the previous item's foreground or background color respectively if available or `none` otherwise.
|
||||
`inverted` swaps the background and foreground colors. The order of words in the string does not matter.
|
||||
|
||||
The `none` token overrides all other tokens in a string if it is not part of a `bg:` specifier, so that e.g. `fg:red none fg:blue` will still create a string with no styling. `bg:none` sets the background to the default color so `fg:red bg:none` is equivalent to `red` or `fg:red` and `bg:green fg:red bg:none` is also equivalent to `fg:red` or `red`. It may become an error to use `none` in conjunction with other tokens in the future.
|
||||
|
||||
|
275
src/config.rs
275
src/config.rs
@ -261,7 +261,7 @@ impl StarshipConfig {
|
||||
}
|
||||
|
||||
/// Deserialize a style string in the starship format with serde
|
||||
pub fn deserialize_style<'de, D>(de: D) -> Result<nu_ansi_term::Style, D::Error>
|
||||
pub fn deserialize_style<'de, D>(de: D) -> Result<Style, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
@ -270,6 +270,88 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum PrevColor {
|
||||
Fg,
|
||||
Bg,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
/// Wrapper for `nu_ansi_term::Style` that supports referencing the previous style's foreground/background color.
|
||||
pub struct Style {
|
||||
style: nu_ansi_term::Style,
|
||||
bg: Option<PrevColor>,
|
||||
fg: Option<PrevColor>,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn to_ansi_style(&self, prev: Option<&nu_ansi_term::Style>) -> nu_ansi_term::Style {
|
||||
let Some(prev_style) = prev else {
|
||||
return self.style;
|
||||
};
|
||||
|
||||
let mut current = self.style;
|
||||
|
||||
if let Some(prev_color) = self.bg {
|
||||
match prev_color {
|
||||
PrevColor::Fg => current.background = prev_style.foreground,
|
||||
PrevColor::Bg => current.background = prev_style.background,
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(prev_color) = self.fg {
|
||||
match prev_color {
|
||||
PrevColor::Fg => current.foreground = prev_style.foreground,
|
||||
PrevColor::Bg => current.foreground = prev_style.background,
|
||||
}
|
||||
}
|
||||
|
||||
current
|
||||
}
|
||||
|
||||
fn map_style<F>(&self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(&nu_ansi_term::Style) -> nu_ansi_term::Style,
|
||||
{
|
||||
Style {
|
||||
style: f(&self.style),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
fn fg(&self, prev_color: PrevColor) -> Self {
|
||||
Self {
|
||||
fg: Some(prev_color),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
fn bg(&self, prev_color: PrevColor) -> Self {
|
||||
Self {
|
||||
bg: Some(prev_color),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nu_ansi_term::Style> for Style {
|
||||
fn from(value: nu_ansi_term::Style) -> Self {
|
||||
Style {
|
||||
style: value,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nu_ansi_term::Color> for Style {
|
||||
fn from(value: nu_ansi_term::Color) -> Self {
|
||||
Style {
|
||||
style: value.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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)
|
||||
@ -279,15 +361,14 @@ where
|
||||
- 'italic'
|
||||
- 'inverted'
|
||||
- 'blink'
|
||||
- 'prev_fg' (specifies the color should be the previous foreground color)
|
||||
- 'prev_bg' (specifies the color should be the previous background color)
|
||||
- '<color>' (see the `parse_color_string` doc for valid color strings)
|
||||
*/
|
||||
pub fn parse_style_string(
|
||||
style_string: &str,
|
||||
context: Option<&Context>,
|
||||
) -> Option<nu_ansi_term::Style> {
|
||||
pub fn parse_style_string(style_string: &str, context: Option<&Context>) -> Option<Style> {
|
||||
style_string
|
||||
.split_whitespace()
|
||||
.try_fold(nu_ansi_term::Style::new(), |style, token| {
|
||||
.try_fold(Style::default(), |style, token| {
|
||||
let token = token.to_lowercase();
|
||||
|
||||
// Check for FG/BG identifiers and strip them off if appropriate
|
||||
@ -301,14 +382,21 @@ pub fn parse_style_string(
|
||||
};
|
||||
|
||||
match token.as_str() {
|
||||
"underline" => Some(style.underline()),
|
||||
"bold" => Some(style.bold()),
|
||||
"italic" => Some(style.italic()),
|
||||
"dimmed" => Some(style.dimmed()),
|
||||
"inverted" => Some(style.reverse()),
|
||||
"blink" => Some(style.blink()),
|
||||
"hidden" => Some(style.hidden()),
|
||||
"strikethrough" => Some(style.strikethrough()),
|
||||
"underline" => Some(style.map_style(|s| s.underline())),
|
||||
"bold" => Some(style.map_style(|s| s.bold())),
|
||||
"italic" => Some(style.map_style(|s| s.italic())),
|
||||
"dimmed" => Some(style.map_style(|s| s.dimmed())),
|
||||
"inverted" => Some(style.map_style(|s| s.reverse())),
|
||||
"blink" => Some(style.map_style(|s| s.blink())),
|
||||
"hidden" => Some(style.map_style(|s| s.hidden())),
|
||||
"strikethrough" => Some(style.map_style(|s| s.strikethrough())),
|
||||
|
||||
"prev_fg" if col_fg => Some(style.fg(PrevColor::Fg)),
|
||||
"prev_fg" => Some(style.bg(PrevColor::Fg)),
|
||||
|
||||
"prev_bg" if col_fg => Some(style.fg(PrevColor::Bg)),
|
||||
"prev_bg" => Some(style.bg(PrevColor::Bg)),
|
||||
|
||||
// When the string is supposed to be a color:
|
||||
// Decide if we yield none, reset background or set color.
|
||||
color_string => {
|
||||
@ -328,15 +416,15 @@ pub fn parse_style_string(
|
||||
// bg + invalid color = reset the background to default.
|
||||
if !col_fg && parsed.is_none() {
|
||||
let mut new_style = style;
|
||||
new_style.background = Option::None;
|
||||
new_style.style.background = Option::None;
|
||||
Some(new_style)
|
||||
} else {
|
||||
// Valid color, apply color to either bg or fg
|
||||
parsed.map(|ansi_color| {
|
||||
if col_fg {
|
||||
style.fg(ansi_color)
|
||||
style.map_style(|s| s.fg(ansi_color))
|
||||
} else {
|
||||
style.on(ansi_color)
|
||||
style.map_style(|s| s.on(ansi_color))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -441,7 +529,7 @@ fn get_palette<'a>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_ansi_term::Style;
|
||||
use nu_ansi_term::Style as AnsiStyle;
|
||||
|
||||
// Small wrapper to allow deserializing Style without a struct with #[serde(deserialize_with=)]
|
||||
#[derive(Default, Clone, Debug, PartialEq)]
|
||||
@ -514,7 +602,7 @@ mod tests {
|
||||
git_status_config.modified,
|
||||
SegmentDisplayConfig {
|
||||
value: "∙",
|
||||
style: Color::Red.normal(),
|
||||
style: Color::Red.normal().into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -624,7 +712,7 @@ mod tests {
|
||||
let config = Value::from("red bold");
|
||||
assert_eq!(
|
||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||
Color::Red.bold()
|
||||
Color::Red.bold().into()
|
||||
);
|
||||
}
|
||||
|
||||
@ -662,13 +750,13 @@ mod tests {
|
||||
fn table_get_styles_bold_italic_underline_green_dimmed_silly_caps() {
|
||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD");
|
||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert!(mystyle.is_bold);
|
||||
assert!(mystyle.is_italic);
|
||||
assert!(mystyle.is_underline);
|
||||
assert!(mystyle.is_dimmed);
|
||||
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||
assert_eq!(
|
||||
mystyle,
|
||||
nu_ansi_term::Style::new()
|
||||
mystyle.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.bold()
|
||||
.italic()
|
||||
.underline()
|
||||
@ -681,14 +769,14 @@ mod tests {
|
||||
fn table_get_styles_bold_italic_underline_green_dimmed_inverted_silly_caps() {
|
||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD InVeRTed");
|
||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert!(mystyle.is_bold);
|
||||
assert!(mystyle.is_italic);
|
||||
assert!(mystyle.is_underline);
|
||||
assert!(mystyle.is_dimmed);
|
||||
assert!(mystyle.is_reverse);
|
||||
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||
assert!(mystyle.to_ansi_style(None).is_reverse);
|
||||
assert_eq!(
|
||||
mystyle,
|
||||
nu_ansi_term::Style::new()
|
||||
mystyle.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.bold()
|
||||
.italic()
|
||||
.underline()
|
||||
@ -702,14 +790,14 @@ mod tests {
|
||||
fn table_get_styles_bold_italic_underline_green_dimmed_blink_silly_caps() {
|
||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD bLiNk");
|
||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert!(mystyle.is_bold);
|
||||
assert!(mystyle.is_italic);
|
||||
assert!(mystyle.is_underline);
|
||||
assert!(mystyle.is_dimmed);
|
||||
assert!(mystyle.is_blink);
|
||||
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||
assert!(mystyle.to_ansi_style(None).is_blink);
|
||||
assert_eq!(
|
||||
mystyle,
|
||||
nu_ansi_term::Style::new()
|
||||
mystyle.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.bold()
|
||||
.italic()
|
||||
.underline()
|
||||
@ -723,14 +811,14 @@ mod tests {
|
||||
fn table_get_styles_bold_italic_underline_green_dimmed_hidden_silly_caps() {
|
||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD hIDDen");
|
||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert!(mystyle.is_bold);
|
||||
assert!(mystyle.is_italic);
|
||||
assert!(mystyle.is_underline);
|
||||
assert!(mystyle.is_dimmed);
|
||||
assert!(mystyle.is_hidden);
|
||||
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||
assert!(mystyle.to_ansi_style(None).is_hidden);
|
||||
assert_eq!(
|
||||
mystyle,
|
||||
nu_ansi_term::Style::new()
|
||||
mystyle.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.bold()
|
||||
.italic()
|
||||
.underline()
|
||||
@ -744,14 +832,14 @@ mod tests {
|
||||
fn table_get_styles_bold_italic_underline_green_dimmed_strikethrough_silly_caps() {
|
||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD StRiKEthROUgh");
|
||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert!(mystyle.is_bold);
|
||||
assert!(mystyle.is_italic);
|
||||
assert!(mystyle.is_underline);
|
||||
assert!(mystyle.is_dimmed);
|
||||
assert!(mystyle.is_strikethrough);
|
||||
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||
assert!(mystyle.to_ansi_style(None).is_strikethrough);
|
||||
assert_eq!(
|
||||
mystyle,
|
||||
nu_ansi_term::Style::new()
|
||||
mystyle.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.bold()
|
||||
.italic()
|
||||
.underline()
|
||||
@ -766,7 +854,7 @@ mod tests {
|
||||
// Test a "plain" style with no formatting
|
||||
let config = Value::from("");
|
||||
let plain_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert_eq!(plain_style, nu_ansi_term::Style::new());
|
||||
assert_eq!(plain_style.to_ansi_style(None), AnsiStyle::new());
|
||||
|
||||
// Test a string that's clearly broken
|
||||
let config = Value::from("djklgfhjkldhlhk;j");
|
||||
@ -803,21 +891,84 @@ mod tests {
|
||||
let config = Value::from("fg:red bg:none");
|
||||
assert_eq!(
|
||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||
Color::Red.normal()
|
||||
Color::Red.normal().into()
|
||||
);
|
||||
|
||||
// Test that bg:none will yield a style
|
||||
let config = Value::from("fg:red bg:none bold");
|
||||
assert_eq!(
|
||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||
Color::Red.bold()
|
||||
Color::Red.bold().into()
|
||||
);
|
||||
|
||||
// Test that bg:none will overwrite the previous background colour
|
||||
let config = Value::from("fg:red bg:green bold bg:none");
|
||||
assert_eq!(
|
||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||
Color::Red.bold()
|
||||
Color::Red.bold().into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_get_styles_previous() {
|
||||
// Test that previous has no effect when there is no previous style
|
||||
let both_prevfg = <StyleWrapper>::from_config(&Value::from(
|
||||
"bold fg:black fg:prev_bg bg:prev_fg underline",
|
||||
))
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
assert_eq!(
|
||||
both_prevfg.to_ansi_style(None),
|
||||
AnsiStyle::default().fg(Color::Black).bold().underline()
|
||||
);
|
||||
|
||||
// But if there is a style on the previous string, then use that
|
||||
let prev_style = AnsiStyle::new()
|
||||
.underline()
|
||||
.fg(Color::Yellow)
|
||||
.on(Color::Red);
|
||||
|
||||
assert_eq!(
|
||||
both_prevfg.to_ansi_style(Some(&prev_style)),
|
||||
AnsiStyle::new()
|
||||
.fg(Color::Red)
|
||||
.on(Color::Yellow)
|
||||
.bold()
|
||||
.underline()
|
||||
);
|
||||
|
||||
// Test that all the combinations of previous colors work
|
||||
let fg_prev_fg = <StyleWrapper>::from_config(&Value::from("fg:prev_fg"))
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
fg_prev_fg.to_ansi_style(Some(&prev_style)),
|
||||
AnsiStyle::new().fg(Color::Yellow)
|
||||
);
|
||||
|
||||
let fg_prev_bg = <StyleWrapper>::from_config(&Value::from("fg:prev_bg"))
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
fg_prev_bg.to_ansi_style(Some(&prev_style)),
|
||||
AnsiStyle::new().fg(Color::Red)
|
||||
);
|
||||
|
||||
let bg_prev_fg = <StyleWrapper>::from_config(&Value::from("bg:prev_fg"))
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
bg_prev_fg.to_ansi_style(Some(&prev_style)),
|
||||
AnsiStyle::new().on(Color::Yellow)
|
||||
);
|
||||
|
||||
let bg_prev_bg = <StyleWrapper>::from_config(&Value::from("bg:prev_bg"))
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
bg_prev_bg.to_ansi_style(Some(&prev_style)),
|
||||
AnsiStyle::new().on(Color::Red)
|
||||
);
|
||||
}
|
||||
|
||||
@ -827,8 +978,8 @@ mod tests {
|
||||
let config = Value::from("bg:#050505 underline fg:120");
|
||||
let flipped_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert_eq!(
|
||||
flipped_style,
|
||||
Style::new()
|
||||
flipped_style.to_ansi_style(None),
|
||||
AnsiStyle::new()
|
||||
.underline()
|
||||
.fg(Color::Fixed(120))
|
||||
.on(Color::Rgb(5, 5, 5))
|
||||
@ -838,8 +989,8 @@ mod tests {
|
||||
let config = Value::from("bg:120 bg:125 bg:127 fg:127 122 125");
|
||||
let multi_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||
assert_eq!(
|
||||
multi_style,
|
||||
Style::new().fg(Color::Fixed(125)).on(Color::Fixed(127))
|
||||
multi_style.to_ansi_style(None),
|
||||
AnsiStyle::new().fg(Color::Fixed(125)).on(Color::Fixed(127))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use nu_ansi_term::Style;
|
||||
use pest::error::Error as PestError;
|
||||
use rayon::prelude::*;
|
||||
use std::borrow::Cow;
|
||||
@ -6,7 +5,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use crate::config::parse_style_string;
|
||||
use crate::config::{parse_style_string, Style};
|
||||
use crate::context::{Context, Shell};
|
||||
use crate::segment::Segment;
|
||||
|
||||
@ -487,7 +486,7 @@ mod tests {
|
||||
let style = Some(Color::Red.bold());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(style, None).unwrap();
|
||||
let result = formatter.parse(style.map(|s| s.into()), None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", style);
|
||||
}
|
||||
@ -562,7 +561,9 @@ 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, None).unwrap();
|
||||
let result = formatter
|
||||
.parse(outer_style.map(|s| s.into()), None)
|
||||
.unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "outer ", outer_style);
|
||||
match_next!(result_iter, "middle ", middle_style);
|
||||
@ -618,9 +619,9 @@ mod tests {
|
||||
|
||||
let mut segments: Vec<Segment> = Vec::new();
|
||||
segments.extend(Segment::from_text(None, "styless"));
|
||||
segments.extend(Segment::from_text(styled_style, "styled"));
|
||||
segments.extend(Segment::from_text(styled_style.map(|s| s.into()), "styled"));
|
||||
segments.extend(Segment::from_text(
|
||||
styled_no_modifier_style,
|
||||
styled_no_modifier_style.map(|s| s.into()),
|
||||
"styled_no_modifier",
|
||||
));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::segment;
|
||||
use crate::segment::{FillSegment, Segment};
|
||||
use nu_ansi_term::{AnsiString, AnsiStrings};
|
||||
use nu_ansi_term::{AnsiString, AnsiStrings, Style as AnsiStyle};
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -190,16 +190,21 @@ where
|
||||
let mut used = 0usize;
|
||||
let mut current: Vec<AnsiString> = Vec::new();
|
||||
let mut chunks: Vec<(Vec<AnsiString>, &FillSegment)> = Vec::new();
|
||||
let mut prev_style: Option<AnsiStyle> = None;
|
||||
|
||||
for segment in segments {
|
||||
match segment {
|
||||
Segment::Fill(fs) => {
|
||||
chunks.push((current, fs));
|
||||
current = Vec::new();
|
||||
prev_style = None;
|
||||
}
|
||||
_ => {
|
||||
used += segment.width_graphemes();
|
||||
current.push(segment.ansi_string());
|
||||
let current_segment_string = segment.ansi_string(prev_style.as_ref());
|
||||
|
||||
prev_style = Some(*current_segment_string.style_ref());
|
||||
current.push(current_segment_string);
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,8 +222,9 @@ where
|
||||
chunks
|
||||
.into_iter()
|
||||
.flat_map(|(strs, fill)| {
|
||||
strs.into_iter()
|
||||
.chain(std::iter::once(fill.ansi_string(fill_size)))
|
||||
let fill_string =
|
||||
fill.ansi_string(fill_size, strs.last().map(|segment| segment.style_ref()));
|
||||
strs.into_iter().chain(std::iter::once(fill_string))
|
||||
})
|
||||
.chain(current)
|
||||
.collect::<Vec<AnsiString>>()
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::print::{Grapheme, UnicodeWidthGraphemes};
|
||||
use nu_ansi_term::{AnsiString, Style};
|
||||
use std::fmt;
|
||||
use crate::{
|
||||
config::Style,
|
||||
print::{Grapheme, UnicodeWidthGraphemes},
|
||||
};
|
||||
use nu_ansi_term::{AnsiString, Style as AnsiStyle};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
/// Type that holds text with an associated style
|
||||
@ -15,9 +17,9 @@ pub struct TextSegment {
|
||||
|
||||
impl TextSegment {
|
||||
// Returns the AnsiString of the segment value
|
||||
fn ansi_string(&self) -> AnsiString {
|
||||
fn ansi_string(&self, prev: Option<&AnsiStyle>) -> AnsiString {
|
||||
match self.style {
|
||||
Some(style) => style.paint(&self.value),
|
||||
Some(style) => style.to_ansi_style(prev).paint(&self.value),
|
||||
None => AnsiString::from(&self.value),
|
||||
}
|
||||
}
|
||||
@ -35,7 +37,7 @@ pub struct FillSegment {
|
||||
|
||||
impl FillSegment {
|
||||
// Returns the AnsiString of the segment value, not including its prefix and suffix
|
||||
pub fn ansi_string(&self, width: Option<usize>) -> AnsiString {
|
||||
pub fn ansi_string(&self, width: Option<usize>, prev: Option<&AnsiStyle>) -> AnsiString {
|
||||
let s = match width {
|
||||
Some(w) => self
|
||||
.value
|
||||
@ -53,7 +55,7 @@ impl FillSegment {
|
||||
None => String::from(&self.value),
|
||||
};
|
||||
match self.style {
|
||||
Some(style) => style.paint(s),
|
||||
Some(style) => style.to_ansi_style(prev).paint(s),
|
||||
None => AnsiString::from(s),
|
||||
}
|
||||
}
|
||||
@ -80,9 +82,9 @@ mod fill_seg_tests {
|
||||
for (text, expected) in &inputs {
|
||||
let f = FillSegment {
|
||||
value: String::from(*text),
|
||||
style: Some(style),
|
||||
style: Some(style.into()),
|
||||
};
|
||||
let actual = f.ansi_string(Some(width));
|
||||
let actual = f.ansi_string(Some(width), None);
|
||||
assert_eq!(style.paint(*expected), actual);
|
||||
}
|
||||
}
|
||||
@ -126,10 +128,10 @@ impl Segment {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn style(&self) -> Option<Style> {
|
||||
pub fn style(&self) -> Option<AnsiStyle> {
|
||||
match self {
|
||||
Self::Fill(fs) => fs.style,
|
||||
Self::Text(ts) => ts.style,
|
||||
Self::Fill(fs) => fs.style.map(|cs| cs.to_ansi_style(None).to_owned()),
|
||||
Self::Text(ts) => ts.style.map(|cs| cs.to_ansi_style(None).to_owned()),
|
||||
Self::LineTerm => None,
|
||||
}
|
||||
}
|
||||
@ -159,10 +161,10 @@ impl Segment {
|
||||
}
|
||||
|
||||
// Returns the AnsiString of the segment value, not including its prefix and suffix
|
||||
pub fn ansi_string(&self) -> AnsiString {
|
||||
pub fn ansi_string(&self, prev: Option<&AnsiStyle>) -> AnsiString {
|
||||
match self {
|
||||
Self::Fill(fs) => fs.ansi_string(None),
|
||||
Self::Text(ts) => ts.ansi_string(),
|
||||
Self::Fill(fs) => fs.ansi_string(None, prev),
|
||||
Self::Text(ts) => ts.ansi_string(prev),
|
||||
Self::LineTerm => AnsiString::from(LINE_TERMINATOR_STRING),
|
||||
}
|
||||
}
|
||||
@ -178,9 +180,3 @@ impl Segment {
|
||||
|
||||
const LINE_TERMINATOR: char = '\n';
|
||||
const LINE_TERMINATOR_STRING: &str = "\n";
|
||||
|
||||
impl fmt::Display for Segment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.ansi_string())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user