Skip to content

Commit

Permalink
Fix Re-enabling manual Style creation (#47)
Browse files Browse the repository at this point in the history
* Fix Style breakage

Move OSControl out of Style

PR #43 created a regression where code that manually instantiated
a Style could no longer be created without using the update
operator and Default trait on Style.

Rather than place non-public functions and data in Style move it
all into AnsiGenericString. This has the added benefit of greatly
simplifying the code.

Fixes #46

* testing: Add manual instance test for Style

PR #43 introduced a pub(crate) field in Style which broke the
intended API (See: Issue #46). Introduce a new test which will fail in those
cases since it won't be able to initialize pub(crate) fields.

Inspired by: #46 (comment)

* Add examples of OSC usage

* CI: Run OSC examples

---------

Co-authored-by: Matt Helsley <matt.helsley+oss@gmail.com>
  • Loading branch information
mhelsley and Matt Helsley authored Jun 28, 2023
1 parent 98b763f commit 76e507c
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 349 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ jobs:
- run: cargo fmt --check --all
- run: cargo clippy -- -D warnings
- run: cargo run --example 256_colors
- run: cargo run --example hyperlink
- run: cargo run --example title
15 changes: 15 additions & 0 deletions examples/hyperlink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use nu_ansi_term::Color;
mod may_sleep;
use may_sleep::{parse_cmd_args, sleep};

fn main() {
#[cfg(windows)]
nu_ansi_term::enable_ansi_support().unwrap();

let sleep_ms = parse_cmd_args();
let mut link = Color::Blue.underline().paint("Link to example.com");
link.hyperlink("https://example.com");

println!("{}", link);
sleep(sleep_ms);
}
35 changes: 35 additions & 0 deletions examples/may_sleep/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pub fn parse_cmd_args() -> Option<u16> {
let mut sleep_ms: Option<u16> = None;
let mut skip_next = false;

for (i, arg) in std::env::args().skip(1).enumerate() {
if skip_next {
skip_next = false;
continue;
}

match &arg[..] {
"-s" | "--sleep" => {
sleep_ms = std::env::args()
.nth(i + 2) // next is +2 because .skip(1)
.unwrap_or(String::from("5000u16"))
.parse::<u16>()
.ok()
.and_then(|parsed| {
skip_next = true;
Some(parsed)
});
}
_ => {}
}
}

sleep_ms
}

pub fn sleep(sleep_ms: Option<u16>) {
if let Some(sleep_ms) = sleep_ms {
let sleep_ms = std::time::Duration::from_millis(sleep_ms as u64);
std::thread::sleep(sleep_ms);
}
}
19 changes: 19 additions & 0 deletions examples/title.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use nu_ansi_term::AnsiGenericString;
mod may_sleep;
use may_sleep::{parse_cmd_args, sleep};

fn main() {
#[cfg(windows)]
nu_ansi_term::enable_ansi_support().unwrap();

let sleep_ms = parse_cmd_args();
let title = AnsiGenericString::title("My Title");
println!(
"{}Terminal title set for the next {:?} milliseconds",
title, sleep_ms
);

// sleep because often prompts change this before you can see
// the results
sleep(sleep_ms);
}
176 changes: 58 additions & 118 deletions src/ansi.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![allow(missing_docs)]
use crate::style::{Color, OSControl, Style};
use crate::style::{Color, Style};
use crate::write::AnyWrite;
use std::fmt;

Expand All @@ -13,95 +13,75 @@ impl Style {
return Ok(());
}

if self.has_sgr() {
// Prefix everything with reset characters if needed
if self.with_reset {
write!(f, "\x1B[0m")?
}
// Prefix everything with reset characters if needed
if self.with_reset {
write!(f, "\x1B[0m")?
}

// "Specified Graphical Rendition" prefixes
// Write the codes’ prefix, then write numbers, separated by
// semicolons, for each text style we want to apply.
write!(f, "\x1B[")?;
let mut written_anything = false;

{
let mut write_char = |c| {
if written_anything {
write!(f, ";")?;
}
written_anything = true;
#[cfg(feature = "gnu_legacy")]
write!(f, "0")?;
write!(f, "{}", c)?;
Ok(())
};

if self.is_bold {
write_char('1')?
}
if self.is_dimmed {
write_char('2')?
}
if self.is_italic {
write_char('3')?
}
if self.is_underline {
write_char('4')?
}
if self.is_blink {
write_char('5')?
}
if self.is_reverse {
write_char('7')?
}
if self.is_hidden {
write_char('8')?
}
if self.is_strikethrough {
write_char('9')?
}
}
// Write the codes’ prefix, then write numbers, separated by
// semicolons, for each text style we want to apply.
write!(f, "\x1B[")?;
let mut written_anything = false;

// The foreground and background colors, if specified, need to be
// handled specially because the number codes are more complicated.
// (see `write_background_code` and `write_foreground_code`)
if let Some(bg) = self.background {
{
let mut write_char = |c| {
if written_anything {
write!(f, ";")?;
}
written_anything = true;
bg.write_background_code(f)?;
#[cfg(feature = "gnu_legacy")]
write!(f, "0")?;
write!(f, "{}", c)?;
Ok(())
};

if self.is_bold {
write_char('1')?
}

if let Some(fg) = self.foreground {
if written_anything {
write!(f, ";")?;
}
fg.write_foreground_code(f)?;
if self.is_dimmed {
write_char('2')?
}

// All the SGR codes end with an `m`, because reasons.
write!(f, "m")?;
}

// OS Control (OSC) prefixes
match self.oscontrol {
Some(OSControl::Hyperlink) => {
write!(f, "\x1B]8;;")?;
if self.is_italic {
write_char('3')?
}
if self.is_underline {
write_char('4')?
}
if self.is_blink {
write_char('5')?
}
Some(OSControl::Title) => {
write!(f, "\x1B]2;")?;
if self.is_reverse {
write_char('7')?
}
Some(OSControl::Icon) => {
write!(f, "\x1B]I;")?;
if self.is_hidden {
write_char('8')?
}
Some(OSControl::Cwd) => {
write!(f, "\x1B]7;")?;
if self.is_strikethrough {
write_char('9')?
}
}

// The foreground and background colors, if specified, need to be
// handled specially because the number codes are more complicated.
// (see `write_background_code` and `write_foreground_code`)
if let Some(bg) = self.background {
if written_anything {
write!(f, ";")?;
}
written_anything = true;
bg.write_background_code(f)?;
}

if let Some(fg) = self.foreground {
if written_anything {
write!(f, ";")?;
}
None => {}
fg.write_foreground_code(f)?;
}

// All the codes end with an `m`, because reasons.
write!(f, "m")?;

Ok(())
}

Expand All @@ -110,28 +90,14 @@ impl Style {
if self.is_plain() {
Ok(())
} else {
match self.oscontrol {
Some(OSControl::Hyperlink) => {
write!(f, "{}{}", HYPERLINK_RESET, RESET)
}
Some(OSControl::Title) | Some(OSControl::Icon) | Some(OSControl::Cwd) => {
write!(f, "{}", ST)
}
_ => {
write!(f, "{}", RESET)
}
}
write!(f, "{}", RESET)
}
}
}

/// The code to send to reset all styles and return to `Style::default()`.
pub static RESET: &str = "\x1B[0m";

// The "String Termination" code. Used for OS Control (OSC) sequences.
static ST: &str = "\x1B\\";
pub(crate) static HYPERLINK_RESET: &str = "\x1B]8;;\x1B\\";

impl Color {
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
match self {
Expand Down Expand Up @@ -396,33 +362,7 @@ impl fmt::Display for Infix {
}
Difference::Reset => {
let f: &mut dyn fmt::Write = f;

match (self.0, self.1) {
(
Style {
oscontrol: Some(OSControl::Hyperlink),
..
},
Style {
oscontrol: None, ..
},
) => {
write!(f, "{}{}", HYPERLINK_RESET, self.1.prefix())
}
(
Style {
oscontrol: Some(_), ..
},
Style {
oscontrol: None, ..
},
) => {
write!(f, "{}{}", ST, self.1.prefix())
}
(_, _) => {
write!(f, "{}{}", RESET, self.1.prefix())
}
}
write!(f, "{}{}", RESET, self.1.prefix())
}
Difference::Empty => {
Ok(()) // nothing to write
Expand Down
12 changes: 0 additions & 12 deletions src/difference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ impl Difference {
return Empty;
}

if first.has_sgr() && !next.has_sgr() {
return Reset;
}

// Cannot un-bold, so must Reset.
if first.is_bold && !next.is_bold {
return Reset;
Expand Down Expand Up @@ -91,10 +87,6 @@ impl Difference {
return Reset;
}

if first.oscontrol.is_some() && next.oscontrol.is_none() {
return Reset;
}

let mut extra_styles = Style::default();

if first.is_bold != next.is_bold {
Expand Down Expand Up @@ -137,10 +129,6 @@ impl Difference {
extra_styles.background = next.background;
}

if first.oscontrol != next.oscontrol {
extra_styles.oscontrol = next.oscontrol;
}

ExtraStyles(extra_styles)
}
}
Expand Down
Loading

0 comments on commit 76e507c

Please sign in to comment.