mirror of
https://github.com/starship/starship.git
synced 2024-11-25 09:43:36 +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>`
|
- `<color>`
|
||||||
- `none`
|
- `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.
|
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
|
/// 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
|
where
|
||||||
D: Deserializer<'de>,
|
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
|
/** Parse a style string which represents an ansi style. Valid tokens in the style
|
||||||
string include the following:
|
string include the following:
|
||||||
- 'fg:<color>' (specifies that the color read should be a foreground color)
|
- 'fg:<color>' (specifies that the color read should be a foreground color)
|
||||||
@ -279,15 +361,14 @@ where
|
|||||||
- 'italic'
|
- 'italic'
|
||||||
- 'inverted'
|
- 'inverted'
|
||||||
- 'blink'
|
- '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)
|
- '<color>' (see the `parse_color_string` doc for valid color strings)
|
||||||
*/
|
*/
|
||||||
pub fn parse_style_string(
|
pub fn parse_style_string(style_string: &str, context: Option<&Context>) -> Option<Style> {
|
||||||
style_string: &str,
|
|
||||||
context: Option<&Context>,
|
|
||||||
) -> Option<nu_ansi_term::Style> {
|
|
||||||
style_string
|
style_string
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.try_fold(nu_ansi_term::Style::new(), |style, token| {
|
.try_fold(Style::default(), |style, token| {
|
||||||
let token = token.to_lowercase();
|
let token = token.to_lowercase();
|
||||||
|
|
||||||
// Check for FG/BG identifiers and strip them off if appropriate
|
// Check for FG/BG identifiers and strip them off if appropriate
|
||||||
@ -301,14 +382,21 @@ pub fn parse_style_string(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match token.as_str() {
|
match token.as_str() {
|
||||||
"underline" => Some(style.underline()),
|
"underline" => Some(style.map_style(|s| s.underline())),
|
||||||
"bold" => Some(style.bold()),
|
"bold" => Some(style.map_style(|s| s.bold())),
|
||||||
"italic" => Some(style.italic()),
|
"italic" => Some(style.map_style(|s| s.italic())),
|
||||||
"dimmed" => Some(style.dimmed()),
|
"dimmed" => Some(style.map_style(|s| s.dimmed())),
|
||||||
"inverted" => Some(style.reverse()),
|
"inverted" => Some(style.map_style(|s| s.reverse())),
|
||||||
"blink" => Some(style.blink()),
|
"blink" => Some(style.map_style(|s| s.blink())),
|
||||||
"hidden" => Some(style.hidden()),
|
"hidden" => Some(style.map_style(|s| s.hidden())),
|
||||||
"strikethrough" => Some(style.strikethrough()),
|
"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:
|
// When the string is supposed to be a color:
|
||||||
// Decide if we yield none, reset background or set color.
|
// Decide if we yield none, reset background or set color.
|
||||||
color_string => {
|
color_string => {
|
||||||
@ -328,15 +416,15 @@ pub fn parse_style_string(
|
|||||||
// bg + invalid color = reset the background to default.
|
// bg + invalid color = reset the background to default.
|
||||||
if !col_fg && parsed.is_none() {
|
if !col_fg && parsed.is_none() {
|
||||||
let mut new_style = style;
|
let mut new_style = style;
|
||||||
new_style.background = Option::None;
|
new_style.style.background = Option::None;
|
||||||
Some(new_style)
|
Some(new_style)
|
||||||
} else {
|
} else {
|
||||||
// Valid color, apply color to either bg or fg
|
// Valid color, apply color to either bg or fg
|
||||||
parsed.map(|ansi_color| {
|
parsed.map(|ansi_color| {
|
||||||
if col_fg {
|
if col_fg {
|
||||||
style.fg(ansi_color)
|
style.map_style(|s| s.fg(ansi_color))
|
||||||
} else {
|
} else {
|
||||||
style.on(ansi_color)
|
style.map_style(|s| s.on(ansi_color))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -441,7 +529,7 @@ fn get_palette<'a>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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=)]
|
// Small wrapper to allow deserializing Style without a struct with #[serde(deserialize_with=)]
|
||||||
#[derive(Default, Clone, Debug, PartialEq)]
|
#[derive(Default, Clone, Debug, PartialEq)]
|
||||||
@ -514,7 +602,7 @@ mod tests {
|
|||||||
git_status_config.modified,
|
git_status_config.modified,
|
||||||
SegmentDisplayConfig {
|
SegmentDisplayConfig {
|
||||||
value: "∙",
|
value: "∙",
|
||||||
style: Color::Red.normal(),
|
style: Color::Red.normal().into(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -624,7 +712,7 @@ mod tests {
|
|||||||
let config = Value::from("red bold");
|
let config = Value::from("red bold");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
<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() {
|
fn table_get_styles_bold_italic_underline_green_dimmed_silly_caps() {
|
||||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD");
|
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD");
|
||||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert!(mystyle.is_bold);
|
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||||
assert!(mystyle.is_italic);
|
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||||
assert!(mystyle.is_underline);
|
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||||
assert!(mystyle.is_dimmed);
|
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mystyle,
|
mystyle.to_ansi_style(None),
|
||||||
nu_ansi_term::Style::new()
|
AnsiStyle::new()
|
||||||
.bold()
|
.bold()
|
||||||
.italic()
|
.italic()
|
||||||
.underline()
|
.underline()
|
||||||
@ -681,14 +769,14 @@ mod tests {
|
|||||||
fn table_get_styles_bold_italic_underline_green_dimmed_inverted_silly_caps() {
|
fn table_get_styles_bold_italic_underline_green_dimmed_inverted_silly_caps() {
|
||||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD InVeRTed");
|
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD InVeRTed");
|
||||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert!(mystyle.is_bold);
|
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||||
assert!(mystyle.is_italic);
|
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||||
assert!(mystyle.is_underline);
|
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||||
assert!(mystyle.is_dimmed);
|
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||||
assert!(mystyle.is_reverse);
|
assert!(mystyle.to_ansi_style(None).is_reverse);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mystyle,
|
mystyle.to_ansi_style(None),
|
||||||
nu_ansi_term::Style::new()
|
AnsiStyle::new()
|
||||||
.bold()
|
.bold()
|
||||||
.italic()
|
.italic()
|
||||||
.underline()
|
.underline()
|
||||||
@ -702,14 +790,14 @@ mod tests {
|
|||||||
fn table_get_styles_bold_italic_underline_green_dimmed_blink_silly_caps() {
|
fn table_get_styles_bold_italic_underline_green_dimmed_blink_silly_caps() {
|
||||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD bLiNk");
|
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD bLiNk");
|
||||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert!(mystyle.is_bold);
|
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||||
assert!(mystyle.is_italic);
|
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||||
assert!(mystyle.is_underline);
|
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||||
assert!(mystyle.is_dimmed);
|
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||||
assert!(mystyle.is_blink);
|
assert!(mystyle.to_ansi_style(None).is_blink);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mystyle,
|
mystyle.to_ansi_style(None),
|
||||||
nu_ansi_term::Style::new()
|
AnsiStyle::new()
|
||||||
.bold()
|
.bold()
|
||||||
.italic()
|
.italic()
|
||||||
.underline()
|
.underline()
|
||||||
@ -723,14 +811,14 @@ mod tests {
|
|||||||
fn table_get_styles_bold_italic_underline_green_dimmed_hidden_silly_caps() {
|
fn table_get_styles_bold_italic_underline_green_dimmed_hidden_silly_caps() {
|
||||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD hIDDen");
|
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD hIDDen");
|
||||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert!(mystyle.is_bold);
|
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||||
assert!(mystyle.is_italic);
|
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||||
assert!(mystyle.is_underline);
|
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||||
assert!(mystyle.is_dimmed);
|
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||||
assert!(mystyle.is_hidden);
|
assert!(mystyle.to_ansi_style(None).is_hidden);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mystyle,
|
mystyle.to_ansi_style(None),
|
||||||
nu_ansi_term::Style::new()
|
AnsiStyle::new()
|
||||||
.bold()
|
.bold()
|
||||||
.italic()
|
.italic()
|
||||||
.underline()
|
.underline()
|
||||||
@ -744,14 +832,14 @@ mod tests {
|
|||||||
fn table_get_styles_bold_italic_underline_green_dimmed_strikethrough_silly_caps() {
|
fn table_get_styles_bold_italic_underline_green_dimmed_strikethrough_silly_caps() {
|
||||||
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD StRiKEthROUgh");
|
let config = Value::from("bOlD ItAlIc uNdErLiNe GrEeN diMMeD StRiKEthROUgh");
|
||||||
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
let mystyle = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert!(mystyle.is_bold);
|
assert!(mystyle.to_ansi_style(None).is_bold);
|
||||||
assert!(mystyle.is_italic);
|
assert!(mystyle.to_ansi_style(None).is_italic);
|
||||||
assert!(mystyle.is_underline);
|
assert!(mystyle.to_ansi_style(None).is_underline);
|
||||||
assert!(mystyle.is_dimmed);
|
assert!(mystyle.to_ansi_style(None).is_dimmed);
|
||||||
assert!(mystyle.is_strikethrough);
|
assert!(mystyle.to_ansi_style(None).is_strikethrough);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mystyle,
|
mystyle.to_ansi_style(None),
|
||||||
nu_ansi_term::Style::new()
|
AnsiStyle::new()
|
||||||
.bold()
|
.bold()
|
||||||
.italic()
|
.italic()
|
||||||
.underline()
|
.underline()
|
||||||
@ -766,7 +854,7 @@ mod tests {
|
|||||||
// Test a "plain" style with no formatting
|
// Test a "plain" style with no formatting
|
||||||
let config = Value::from("");
|
let config = Value::from("");
|
||||||
let plain_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
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
|
// Test a string that's clearly broken
|
||||||
let config = Value::from("djklgfhjkldhlhk;j");
|
let config = Value::from("djklgfhjkldhlhk;j");
|
||||||
@ -803,21 +891,84 @@ mod tests {
|
|||||||
let config = Value::from("fg:red bg:none");
|
let config = Value::from("fg:red bg:none");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||||
Color::Red.normal()
|
Color::Red.normal().into()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test that bg:none will yield a style
|
// Test that bg:none will yield a style
|
||||||
let config = Value::from("fg:red bg:none bold");
|
let config = Value::from("fg:red bg:none bold");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
<StyleWrapper>::from_config(&config).unwrap().0,
|
||||||
Color::Red.bold()
|
Color::Red.bold().into()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test that bg:none will overwrite the previous background colour
|
// Test that bg:none will overwrite the previous background colour
|
||||||
let config = Value::from("fg:red bg:green bold bg:none");
|
let config = Value::from("fg:red bg:green bold bg:none");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
<StyleWrapper>::from_config(&config).unwrap().0,
|
<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 config = Value::from("bg:#050505 underline fg:120");
|
||||||
let flipped_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
let flipped_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
flipped_style,
|
flipped_style.to_ansi_style(None),
|
||||||
Style::new()
|
AnsiStyle::new()
|
||||||
.underline()
|
.underline()
|
||||||
.fg(Color::Fixed(120))
|
.fg(Color::Fixed(120))
|
||||||
.on(Color::Rgb(5, 5, 5))
|
.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 config = Value::from("bg:120 bg:125 bg:127 fg:127 122 125");
|
||||||
let multi_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
let multi_style = <StyleWrapper>::from_config(&config).unwrap().0;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
multi_style,
|
multi_style.to_ansi_style(None),
|
||||||
Style::new().fg(Color::Fixed(125)).on(Color::Fixed(127))
|
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 pest::error::Error as PestError;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -6,7 +5,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::config::parse_style_string;
|
use crate::config::{parse_style_string, Style};
|
||||||
use crate::context::{Context, Shell};
|
use crate::context::{Context, Shell};
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
|
|
||||||
@ -487,7 +486,7 @@ mod tests {
|
|||||||
let style = Some(Color::Red.bold());
|
let style = Some(Color::Red.bold());
|
||||||
|
|
||||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
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();
|
let mut result_iter = result.iter();
|
||||||
match_next!(result_iter, "text", style);
|
match_next!(result_iter, "text", style);
|
||||||
}
|
}
|
||||||
@ -562,7 +561,9 @@ mod tests {
|
|||||||
let inner_style = Some(Color::Blue.normal());
|
let inner_style = Some(Color::Blue.normal());
|
||||||
|
|
||||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
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();
|
let mut result_iter = result.iter();
|
||||||
match_next!(result_iter, "outer ", outer_style);
|
match_next!(result_iter, "outer ", outer_style);
|
||||||
match_next!(result_iter, "middle ", middle_style);
|
match_next!(result_iter, "middle ", middle_style);
|
||||||
@ -618,9 +619,9 @@ mod tests {
|
|||||||
|
|
||||||
let mut segments: Vec<Segment> = Vec::new();
|
let mut segments: Vec<Segment> = Vec::new();
|
||||||
segments.extend(Segment::from_text(None, "styless"));
|
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(
|
segments.extend(Segment::from_text(
|
||||||
styled_no_modifier_style,
|
styled_no_modifier_style.map(|s| s.into()),
|
||||||
"styled_no_modifier",
|
"styled_no_modifier",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::segment;
|
use crate::segment;
|
||||||
use crate::segment::{FillSegment, 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::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -190,16 +190,21 @@ where
|
|||||||
let mut used = 0usize;
|
let mut used = 0usize;
|
||||||
let mut current: Vec<AnsiString> = Vec::new();
|
let mut current: Vec<AnsiString> = Vec::new();
|
||||||
let mut chunks: Vec<(Vec<AnsiString>, &FillSegment)> = Vec::new();
|
let mut chunks: Vec<(Vec<AnsiString>, &FillSegment)> = Vec::new();
|
||||||
|
let mut prev_style: Option<AnsiStyle> = None;
|
||||||
|
|
||||||
for segment in segments {
|
for segment in segments {
|
||||||
match segment {
|
match segment {
|
||||||
Segment::Fill(fs) => {
|
Segment::Fill(fs) => {
|
||||||
chunks.push((current, fs));
|
chunks.push((current, fs));
|
||||||
current = Vec::new();
|
current = Vec::new();
|
||||||
|
prev_style = None;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
used += segment.width_graphemes();
|
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
|
chunks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|(strs, fill)| {
|
.flat_map(|(strs, fill)| {
|
||||||
strs.into_iter()
|
let fill_string =
|
||||||
.chain(std::iter::once(fill.ansi_string(fill_size)))
|
fill.ansi_string(fill_size, strs.last().map(|segment| segment.style_ref()));
|
||||||
|
strs.into_iter().chain(std::iter::once(fill_string))
|
||||||
})
|
})
|
||||||
.chain(current)
|
.chain(current)
|
||||||
.collect::<Vec<AnsiString>>()
|
.collect::<Vec<AnsiString>>()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use crate::print::{Grapheme, UnicodeWidthGraphemes};
|
use crate::{
|
||||||
use nu_ansi_term::{AnsiString, Style};
|
config::Style,
|
||||||
use std::fmt;
|
print::{Grapheme, UnicodeWidthGraphemes},
|
||||||
|
};
|
||||||
|
use nu_ansi_term::{AnsiString, Style as AnsiStyle};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
/// Type that holds text with an associated style
|
/// Type that holds text with an associated style
|
||||||
@ -15,9 +17,9 @@ pub struct TextSegment {
|
|||||||
|
|
||||||
impl TextSegment {
|
impl TextSegment {
|
||||||
// Returns the AnsiString of the segment value
|
// Returns the AnsiString of the segment value
|
||||||
fn ansi_string(&self) -> AnsiString {
|
fn ansi_string(&self, prev: Option<&AnsiStyle>) -> AnsiString {
|
||||||
match self.style {
|
match self.style {
|
||||||
Some(style) => style.paint(&self.value),
|
Some(style) => style.to_ansi_style(prev).paint(&self.value),
|
||||||
None => AnsiString::from(&self.value),
|
None => AnsiString::from(&self.value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +37,7 @@ pub struct FillSegment {
|
|||||||
|
|
||||||
impl FillSegment {
|
impl FillSegment {
|
||||||
// Returns the AnsiString of the segment value, not including its prefix and suffix
|
// 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 {
|
let s = match width {
|
||||||
Some(w) => self
|
Some(w) => self
|
||||||
.value
|
.value
|
||||||
@ -53,7 +55,7 @@ impl FillSegment {
|
|||||||
None => String::from(&self.value),
|
None => String::from(&self.value),
|
||||||
};
|
};
|
||||||
match self.style {
|
match self.style {
|
||||||
Some(style) => style.paint(s),
|
Some(style) => style.to_ansi_style(prev).paint(s),
|
||||||
None => AnsiString::from(s),
|
None => AnsiString::from(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,9 +82,9 @@ mod fill_seg_tests {
|
|||||||
for (text, expected) in &inputs {
|
for (text, expected) in &inputs {
|
||||||
let f = FillSegment {
|
let f = FillSegment {
|
||||||
value: String::from(*text),
|
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);
|
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 {
|
match self {
|
||||||
Self::Fill(fs) => fs.style,
|
Self::Fill(fs) => fs.style.map(|cs| cs.to_ansi_style(None).to_owned()),
|
||||||
Self::Text(ts) => ts.style,
|
Self::Text(ts) => ts.style.map(|cs| cs.to_ansi_style(None).to_owned()),
|
||||||
Self::LineTerm => None,
|
Self::LineTerm => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,10 +161,10 @@ impl Segment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the AnsiString of the segment value, not including its prefix and suffix
|
// 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 {
|
match self {
|
||||||
Self::Fill(fs) => fs.ansi_string(None),
|
Self::Fill(fs) => fs.ansi_string(None, prev),
|
||||||
Self::Text(ts) => ts.ansi_string(),
|
Self::Text(ts) => ts.ansi_string(prev),
|
||||||
Self::LineTerm => AnsiString::from(LINE_TERMINATOR_STRING),
|
Self::LineTerm => AnsiString::from(LINE_TERMINATOR_STRING),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,9 +180,3 @@ impl Segment {
|
|||||||
|
|
||||||
const LINE_TERMINATOR: char = '\n';
|
const LINE_TERMINATOR: char = '\n';
|
||||||
const LINE_TERMINATOR_STRING: &str = "\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