2021-02-22 19:33:34 +01:00
|
|
|
|
use crate::ansi::RESET;
|
|
|
|
|
use crate::difference::Difference;
|
|
|
|
|
use crate::style::{Color, Style};
|
|
|
|
|
use crate::write::AnyWrite;
|
|
|
|
|
use std::borrow::Cow;
|
|
|
|
|
use std::fmt;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
|
|
/// An `ANSIGenericString` includes a generic string type and a `Style` to
|
|
|
|
|
/// display that string. `ANSIString` and `ANSIByteString` are aliases for
|
|
|
|
|
/// this type on `str` and `\[u8]`, respectively.
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
|
|
|
|
style: Style,
|
|
|
|
|
string: Cow<'a, S>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Cloning an `ANSIGenericString` will clone its underlying string.
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use nu_ansi_term::ANSIString;
|
|
|
|
|
///
|
|
|
|
|
/// let plain_string = ANSIString::from("a plain string");
|
|
|
|
|
/// let clone_string = plain_string.clone();
|
|
|
|
|
/// assert_eq!(clone_string, plain_string);
|
|
|
|
|
/// ```
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
2021-03-26 09:26:57 +01:00
|
|
|
|
fn clone(&self) -> AnsiGenericString<'a, S> {
|
|
|
|
|
AnsiGenericString {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
style: self.style,
|
|
|
|
|
string: self.string.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// You might think that the hand-written Clone impl above is the same as the
|
|
|
|
|
// one that gets generated with #[derive]. But it’s not *quite* the same!
|
|
|
|
|
//
|
|
|
|
|
// `str` is not Clone, and the derived Clone implementation puts a Clone
|
|
|
|
|
// constraint on the S type parameter (generated using --pretty=expanded):
|
|
|
|
|
//
|
|
|
|
|
// ↓_________________↓
|
|
|
|
|
// impl <'a, S: ::std::clone::Clone + 'a + ToOwned + ?Sized> ::std::clone::Clone
|
|
|
|
|
// for ANSIGenericString<'a, S> where
|
|
|
|
|
// <S as ToOwned>::Owned: fmt::Debug { ... }
|
|
|
|
|
//
|
|
|
|
|
// This resulted in compile errors when you tried to derive Clone on a type
|
|
|
|
|
// that used it:
|
|
|
|
|
//
|
|
|
|
|
// #[derive(PartialEq, Debug, Clone, Default)]
|
|
|
|
|
// pub struct TextCellContents(Vec<ANSIString<'static>>);
|
|
|
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
// error[E0277]: the trait `std::clone::Clone` is not implemented for `str`
|
|
|
|
|
//
|
|
|
|
|
// The hand-written impl above can ignore that constraint and still compile.
|
|
|
|
|
|
|
|
|
|
/// An ANSI String is a string coupled with the `Style` to display it
|
|
|
|
|
/// in a terminal.
|
|
|
|
|
///
|
|
|
|
|
/// Although not technically a string itself, it can be turned into
|
|
|
|
|
/// one with the `to_string` method.
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use nu_ansi_term::ANSIString;
|
|
|
|
|
/// use nu_ansi_term::Color::Red;
|
|
|
|
|
///
|
|
|
|
|
/// let red_string = Red.paint("a red string");
|
|
|
|
|
/// println!("{}", red_string);
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use nu_ansi_term::ANSIString;
|
|
|
|
|
///
|
|
|
|
|
/// let plain_string = ANSIString::from("a plain string");
|
|
|
|
|
/// assert_eq!(&*plain_string, "a plain string");
|
|
|
|
|
/// ```
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub type AnsiString<'a> = AnsiGenericString<'a, str>;
|
2021-02-22 19:33:34 +01:00
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
/// An `AnsiByteString` represents a formatted series of bytes. Use
|
|
|
|
|
/// `AnsiByteString` when styling text with an unknown encoding.
|
|
|
|
|
pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>;
|
2021-02-22 19:33:34 +01:00
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
I: Into<Cow<'a, S>>,
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
2021-03-26 09:26:57 +01:00
|
|
|
|
fn from(input: I) -> AnsiGenericString<'a, S> {
|
|
|
|
|
AnsiGenericString {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
string: input.into(),
|
|
|
|
|
style: Style::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
|
|
|
|
/// Directly access the style
|
|
|
|
|
pub fn style_ref(&self) -> &Style {
|
|
|
|
|
&self.style
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Directly access the style mutably
|
|
|
|
|
pub fn style_ref_mut(&mut self) -> &mut Style {
|
|
|
|
|
&mut self.style
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, S: 'a + ToOwned + ?Sized> Deref for AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
|
|
|
|
type Target = S;
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &S {
|
|
|
|
|
self.string.deref()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
/// A set of `AnsiGenericStrings`s collected together, in order to be
|
2021-02-22 19:33:34 +01:00
|
|
|
|
/// written with a minimum of control characters.
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>])
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
S: PartialEq;
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
/// A set of `AnsiString`s collected together, in order to be written with a
|
2021-02-22 19:33:34 +01:00
|
|
|
|
/// minimum of control characters.
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>;
|
2021-02-22 19:33:34 +01:00
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
/// A function to construct an `AnsiStrings` instance.
|
2021-02-22 19:33:34 +01:00
|
|
|
|
#[allow(non_snake_case)]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> {
|
|
|
|
|
AnsiGenericStrings(arg)
|
2021-02-22 19:33:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
/// A set of `AnsiByteString`s collected together, in order to be
|
2021-02-22 19:33:34 +01:00
|
|
|
|
/// written with a minimum of control characters.
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>;
|
2021-02-22 19:33:34 +01:00
|
|
|
|
|
|
|
|
|
/// A function to construct an `ANSIByteStrings` instance.
|
|
|
|
|
#[allow(non_snake_case)]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub fn ANSIByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> {
|
|
|
|
|
AnsiGenericStrings(arg)
|
2021-02-22 19:33:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- paint functions ----
|
|
|
|
|
|
|
|
|
|
impl Style {
|
|
|
|
|
/// Paints the given text with this color, returning an ANSI string.
|
|
|
|
|
#[must_use]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
I: Into<Cow<'a, S>>,
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
2021-03-26 09:26:57 +01:00
|
|
|
|
AnsiGenericString {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
string: input.into(),
|
|
|
|
|
style: self,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Color {
|
|
|
|
|
/// Paints the given text with this color, returning an ANSI string.
|
|
|
|
|
/// This is a short-cut so you don’t have to use `Blue.normal()` just
|
|
|
|
|
/// to get blue text.
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use nu_ansi_term::Color::Blue;
|
|
|
|
|
/// println!("{}", Blue.paint("da ba dee"));
|
|
|
|
|
/// ```
|
|
|
|
|
#[must_use]
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
I: Into<Cow<'a, S>>,
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
{
|
2021-03-26 09:26:57 +01:00
|
|
|
|
AnsiGenericString {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
string: input.into(),
|
|
|
|
|
style: self.normal(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- writers for individual ANSI strings ----
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a> fmt::Display for AnsiString<'a> {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
let w: &mut dyn fmt::Write = f;
|
|
|
|
|
self.write_to_any(w)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a> AnsiByteString<'a> {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
/// Write an `ANSIByteString` to an `io::Write`. This writes the escape
|
|
|
|
|
/// sequences for the associated `Style` around the bytes.
|
|
|
|
|
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
|
let w: &mut dyn io::Write = w;
|
|
|
|
|
self.write_to_any(w)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
&'a S: AsRef<[u8]>,
|
|
|
|
|
{
|
|
|
|
|
fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
|
|
|
|
|
write!(w, "{}", self.style.prefix())?;
|
|
|
|
|
w.write_str(self.string.as_ref())?;
|
|
|
|
|
write!(w, "{}", self.style.suffix())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- writers for combined ANSI strings ----
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a> fmt::Display for AnsiStrings<'a> {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
let f: &mut dyn fmt::Write = f;
|
|
|
|
|
self.write_to_any(f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a> AnsiByteStrings<'a> {
|
2021-02-22 19:33:34 +01:00
|
|
|
|
/// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal
|
|
|
|
|
/// escape sequences for the associated `Style`s around each set of
|
|
|
|
|
/// bytes.
|
|
|
|
|
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
|
let w: &mut dyn io::Write = w;
|
|
|
|
|
self.write_to_any(w)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:26:57 +01:00
|
|
|
|
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S>
|
2021-02-22 19:33:34 +01:00
|
|
|
|
where
|
|
|
|
|
<S as ToOwned>::Owned: fmt::Debug,
|
|
|
|
|
&'a S: AsRef<[u8]>,
|
|
|
|
|
{
|
|
|
|
|
fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
|
|
|
|
|
use self::Difference::*;
|
|
|
|
|
|
|
|
|
|
let first = match self.0.first() {
|
|
|
|
|
None => return Ok(()),
|
|
|
|
|
Some(f) => f,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
write!(w, "{}", first.style.prefix())?;
|
|
|
|
|
w.write_str(first.string.as_ref())?;
|
|
|
|
|
|
|
|
|
|
for window in self.0.windows(2) {
|
|
|
|
|
match Difference::between(&window[0].style, &window[1].style) {
|
|
|
|
|
ExtraStyles(style) => write!(w, "{}", style.prefix())?,
|
|
|
|
|
Reset => write!(w, "{}{}", RESET, window[1].style.prefix())?,
|
2021-06-09 21:08:12 +02:00
|
|
|
|
Empty => { /* Do nothing! */ }
|
2021-02-22 19:33:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write_str(&window[1].string)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the final reset string after all of the ANSIStrings have been
|
|
|
|
|
// written, *except* if the last one has no styles, because it would
|
|
|
|
|
// have already been written by this point.
|
|
|
|
|
if let Some(last) = self.0.last() {
|
|
|
|
|
if !last.style.is_plain() {
|
|
|
|
|
write!(w, "{}", RESET)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- tests ----
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2021-03-26 09:26:57 +01:00
|
|
|
|
pub use super::super::AnsiStrings;
|
2021-02-22 19:33:34 +01:00
|
|
|
|
pub use crate::style::Color::*;
|
|
|
|
|
pub use crate::style::Style;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn no_control_codes_for_plain() {
|
|
|
|
|
let one = Style::default().paint("one");
|
|
|
|
|
let two = Style::default().paint("two");
|
2021-03-26 09:26:57 +01:00
|
|
|
|
let output = format!("{}", AnsiStrings(&[one, two]));
|
2021-02-22 19:33:34 +01:00
|
|
|
|
assert_eq!(&*output, "onetwo");
|
|
|
|
|
}
|
|
|
|
|
}
|