1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
//! Experimental support for hyperlinking.
//!
//! # Usage
//!
//! Enable the `hyperlink` crate feature. Enabling `hyperlink` implicitly
//! enables `std`.
//!
//! Import the `HyperlinkExt` extension trait and use the [`link()`] builder
//! method.
//!
//! [`link()`]: HyperlinkExt::link()
//!
//! ```rust
//! use yansi::Paint;
//! use yansi::hyperlink::HyperlinkExt;
//!
//! println!("Go to {}.", "our docs".link("https://docs.rs/yansi").green());
//! ```
//!
//! `>` Go to <a href="https://docs.rs/yansi"><span style="color: green;">our docs</span></a>.
//!
//! The `link()` method returns a [`PaintedLink`] structure which implements all
//! of the unverisal chainable methods available across the library.
//! Furthermore, [`Painted`] is extended with a [`link()`](Painted::link())
//! method. The net effect is that you can use `link()` as if it were any other
//! styling method:
//!
//! ```rust
//! use yansi::Paint;
//! use yansi::hyperlink::HyperlinkExt;
//!
//! println!("Go to {}.", "our docs".green().link("https://docs.rs/yansi").on_black().invert());
//! ```
//!
//! `>` Go to <a href="https://docs.rs/yansi">
//! <span style="background: green; color: black;">our docs</span>
//! </a>.
//!
//! # Caveats
//!
//! 1. You can only create a link when there is a target value to print, that
//! is, when the receiver is something "printable". In other words, you
//! _cannot_ apply `link()` to a bare `Style`. This means the following will
//! not work:
//!
//! ```rust,compile_fail
//! use yansi::{Paint, Style, Color::*};
//! use yansi::hyperlink::HyperlinkExt;
//!
//! static LINKED: Style = Green.link("https://docs.rs/yansi");
//! ```
//! <br/>
//!
//! 2. While some modern terminals support hyperlinking, many do not. Those that
//! do not _should_ gracefully ignore the target URL and print the original
//! value. That is, instead of `>` <a href="https://docs.rs/yansi">our
//! docs</a>, such terminals would print `>` our docs.
use core::fmt;
use crate::*;
/// A [`Painted`] with an associated target URL to hyperlink.
pub struct PaintedLink<T> {
painted: Painted<T>,
link: String,
}
/// Extension trait to apply hyperlinks to any value, implemented for all types.
///
/// See the [module level docs](hyperlink) for usage details.
pub trait HyperlinkExt {
/// Create a painted hyperlink with a target URL of `url`.
///
/// See [`hyperlink`] for details.
///
/// # Example
///
/// ```rust
/// use yansi::hyperlink::HyperlinkExt;
///
/// println!("See {}.", "our docs".link("https://docs.rs/yansi"));
/// ```
fn link(&self, url: impl ToString) -> PaintedLink<&Self>;
}
impl<T> PaintedLink<T> {
fn fmt_args(
&self,
fmt: &dyn Fn(&Painted<T>, &mut fmt::Formatter) -> fmt::Result,
f: &mut fmt::Formatter,
_args: fmt::Arguments<'_>,
) -> fmt::Result {
if !self.painted.enabled() {
return fmt(&self.painted, f);
}
write!(f, "\x1B]8;;{}\x1B\\", self.link)?;
fmt(&self.painted, f)?;
write!(f, "\x1B]8;;\x1B\\")
}
}
impl_fmt_traits!(<T> PaintedLink<T> => self.painted (Painted<T>));
impl<T> HyperlinkExt for T {
fn link(&self, url: impl ToString) -> PaintedLink<&Self> {
PaintedLink { painted: Painted::new(self), link: url.to_string() }
}
}
/// Experimental support for hyperlinking.
impl<T> Painted<T> {
/// Create a painted hyperlink with a target URL of `url`.
///
/// See [`hyperlink`] for details.
///
/// # Example
///
/// ```rust
/// use yansi::Paint;
/// use yansi::hyperlink::HyperlinkExt;
///
/// println!("See {}.", "our docs".green().link("https://docs.rs/yansi"));
/// ```
pub fn link(&self, url: impl ToString) -> PaintedLink<&Self> {
PaintedLink { painted: Painted::new(self), link: url.to_string() }
}
}
impl<T> PaintedLink<T> {
#[inline(always)]
const fn apply(mut self, a: crate::style::Application) -> Self {
self.painted.style = self.painted.style.apply(a);
self
}
properties!([pub const] constructor(Self) -> Self);
}