Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ANSI colors in msys terminals. See #2807 #11028

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 97 additions & 103 deletions src/libextra/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@

use std::io::{Decorator, Writer};

#[cfg(not(target_os = "win32"))] use std::os;
#[cfg(not(target_os = "win32"))] use terminfo::*;
#[cfg(not(target_os = "win32"))] use terminfo::searcher::open;
#[cfg(not(target_os = "win32"))] use terminfo::parser::compiled::parse;
#[cfg(not(target_os = "win32"))] use terminfo::parm::{expand, Number, Variables};

// FIXME (#2807): Windows support.
use std::os;
use terminfo::*;
use terminfo::searcher::open;
use terminfo::parser::compiled::parse;
use terminfo::parm::{expand, Number, Variables};

pub mod color {
pub type Color = u16;
Expand Down Expand Up @@ -74,7 +72,6 @@ pub mod attr {
}
}

#[cfg(not(target_os = "win32"))]
fn cap_for_attr(attr: attr::Attr) -> &'static str {
match attr {
attr::Bold => "bold",
Expand All @@ -93,29 +90,24 @@ fn cap_for_attr(attr: attr::Attr) -> &'static str {
}
}

#[cfg(not(target_os = "win32"))]
pub struct Terminal<T> {
priv num_colors: u16,
priv out: T,
priv ti: ~TermInfo
priv ti: Option<~TermInfo>
}

#[cfg(target_os = "win32")]
pub struct Terminal<T> {
priv num_colors: u16,
priv out: T,
}

#[cfg(not(target_os = "win32"))]
impl<T: Writer> Terminal<T> {
pub fn new(out: T) -> Result<Terminal<T>, ~str> {
let term = os::getenv("TERM");
if term.is_none() {
return Err(~"TERM environment variable undefined");
}
let term = match os::getenv("TERM") {
None => return Err(~"TERM environment variable undefined"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: reorder these arms (None/error handling should come last)

Some(t) => t
};

let entry = open(term.unwrap());
let entry = open(term);
if entry.is_err() {
if term == ~"cygwin" { // msys terminal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary allocation, use if term == "cygwin"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need to be if "cygwin" == term to get the coercions happening correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives me:

C:\Users\jhass_000\git\rust\src\libextra\term.rs:108:23: 108:31 error: mismatched types: expected `~str` but found `&'static str` (str storage differs: expected ~ but found &'static )
C:\Users\jhass_000\git\rust\src\libextra\term.rs:108             if term == "cygwin" { // msys terminal
                                                                            ^~~~~~~~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes "cygwin" == term compiles fine.

return Ok(Terminal {out: out, ti: None, num_colors: 16});
}
return Err(entry.unwrap_err());
}

Expand All @@ -130,47 +122,60 @@ impl<T: Writer> Terminal<T> {
inf.numbers.find_equiv(&("colors")).map_default(0, |&n| n)
} else { 0 };

return Ok(Terminal {out: out, ti: inf, num_colors: nc});
return Ok(Terminal {out: out, ti: Some(inf), num_colors: nc});
}

/// Helper function, see fg and bg.
fn set_color(&mut self, color: color::Color, bg: bool) -> bool {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
match self.ti {
None => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again with the arm reorder. Also, a comment that this is for terminals which support ANSI colors but don't distribute terminfo.

let number = if bg { color + 10 } else { color };
let ansi = if number < 8 {
format!("\x1b[{}m", 30 + number)
} else {
format!("\x1b[{};1m", 22 + number)
};
self.out.write(ansi.as_bytes());
return true
},
Some(ref ti) => {
let s = expand(*ti.strings.find_equiv(&(if bg {
"setab"
} else {
"setaf"
})).unwrap(), [Number(color as int)], &mut Variables::new());
if s.is_ok() {
self.out.write(s.unwrap());
return true
} else {
warn!("{}", s.unwrap_err());
}
}
}
}
false
}

/// Sets the foreground color to the given color.
///
/// If the color is a bright color, but the terminal only supports 8 colors,
/// the corresponding normal color will be used instead.
///
/// Returns true if the color was set, false otherwise.
pub fn fg(&mut self, color: color::Color) -> bool {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
self.out.write(s.unwrap());
return true
} else {
warn!("{}", s.unwrap_err());
}
}
false
self.set_color(color, false)
}

/// Sets the background color to the given color.
///
/// If the color is a bright color, but the terminal only supports 8 colors,
/// the corresponding normal color will be used instead.
///
/// Returns true if the color was set, false otherwise.
pub fn bg(&mut self, color: color::Color) -> bool {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
self.out.write(s.unwrap());
return true
} else {
warn!("{}", s.unwrap_err());
}
}
false
self.set_color(color, true)
}

/// Sets the given terminal attribute, if supported.
Expand All @@ -180,18 +185,23 @@ impl<T: Writer> Terminal<T> {
attr::ForegroundColor(c) => self.fg(c),
attr::BackgroundColor(c) => self.bg(c),
_ => {
let cap = cap_for_attr(attr);
let parm = self.ti.strings.find_equiv(&cap);
if parm.is_some() {
let s = expand(*parm.unwrap(), [], &mut Variables::new());
if s.is_ok() {
self.out.write(s.unwrap());
return true
} else {
warn!("{}", s.unwrap_err());
match self.ti {
None => return false,
Some(ref ti) => {
let cap = cap_for_attr(attr);
let parm = ti.strings.find_equiv(&cap);
if parm.is_some() {
let s = expand(*parm.unwrap(), [], &mut Variables::new());
if s.is_ok() {
self.out.write(s.unwrap());
return true
} else {
warn!("{}", s.unwrap_err());
}
}
false
}
}
false
}
}
}
Expand All @@ -204,34 +214,44 @@ impl<T: Writer> Terminal<T> {
}
_ => {
let cap = cap_for_attr(attr);
self.ti.strings.find_equiv(&cap).is_some()
match self.ti {
None => return false,
Some(ref ti) => ti.strings.find_equiv(&cap).is_some()
}
}
}
}

/// Resets all terminal attributes and color to the default.
pub fn reset(&mut self) {
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
if cap.is_none() {
// are there any terminals that have color/attrs and not sgr0?
// Try falling back to sgr, then op
cap = self.ti.strings.find_equiv(&("sgr"));
if cap.is_none() {
cap = self.ti.strings.find_equiv(&("op"));
match self.ti {
None => if self.num_colors > 0 {
self.out.write([27u8, 91u8, 51u8, 57u8, 59u8, 52u8, 57u8, 109u8])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the bytes! macro, ie bytes!(27, "[39;49m")

},
Some(ref ti) => {
let mut cap = ti.strings.find_equiv(&("sgr0"));
if cap.is_none() {
// are there any terminals that have color/attrs and not sgr0?
// Try falling back to sgr, then op
cap = ti.strings.find_equiv(&("sgr"));
if cap.is_none() {
cap = ti.strings.find_equiv(&("op"));
}
}
let s = cap.map_default(Err(~"can't find terminfo capability `sgr0`"), |op| {
expand(*op, [], &mut Variables::new())
});
if s.is_ok() {
self.out.write(s.unwrap());
} else if self.num_colors > 0 {
warn!("{}", s.unwrap_err());
} else {
// if we support attributes but not color, it would be nice to still warn!()
// but it's not worth testing all known attributes just for this.
debug!("{}", s.unwrap_err());
}
}
}
let s = cap.map_default(Err(~"can't find terminfo capability `sgr0`"), |op| {
expand(*op, [], &mut Variables::new())
});
if s.is_ok() {
self.out.write(s.unwrap());
} else if self.num_colors > 0 {
warn!("{}", s.unwrap_err());
} else {
// if we support attributes but not color, it would be nice to still warn!()
// but it's not worth testing all known attributes just for this.
debug!("{}", s.unwrap_err());
}
}

fn dim_if_necessary(&self, color: color::Color) -> color::Color {
Expand All @@ -241,32 +261,6 @@ impl<T: Writer> Terminal<T> {
}
}

#[cfg(target_os = "win32")]
impl<T: Writer> Terminal<T> {
pub fn new(out: T) -> Result<Terminal<T>, ~str> {
return Ok(Terminal {out: out, num_colors: 0});
}

pub fn fg(&mut self, _color: color::Color) -> bool {
false
}

pub fn bg(&mut self, _color: color::Color) -> bool {
false
}

pub fn attr(&mut self, _attr: attr::Attr) -> bool {
false
}

pub fn supports_attr(&self, _attr: attr::Attr) -> bool {
false
}

pub fn reset(&self) {
}
}

impl<T: Writer> Decorator<T> for Terminal<T> {
fn inner(self) -> T {
self.out
Expand Down