From 7d8a0fdb7dafa5dfca003f5418119f0aaaac1484 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 10 Jul 2013 18:26:04 -0700 Subject: [PATCH 1/4] Give term.fg() and term.bg() a bool return value --- src/libextra/term.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libextra/term.rs b/src/libextra/term.rs index cd226e2ad3267..1740f4d1ecc16 100644 --- a/src/libextra/term.rs +++ b/src/libextra/term.rs @@ -88,33 +88,41 @@ impl Terminal { /// /// If the color is a bright color, but the terminal only supports 8 colors, /// the corresponding normal color will be used instead. - pub fn fg(&self, color: color::Color) { + /// + /// Returns true if the color was set, false otherwise. + pub fn fg(&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", s.unwrap_err()); } } + 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. - pub fn bg(&self, color: color::Color) { + /// + /// Rturns true if the color was set, false otherwise. + pub fn bg(&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", s.unwrap_err()); } } + false } pub fn reset(&self) { let mut vars = Variables::new(); @@ -144,10 +152,12 @@ impl Terminal { return Ok(Terminal {out: out, num_colors: 0}); } - pub fn fg(&self, _color: color::Color) { + pub fn fg(&self, _color: color::Color) -> bool { + false } - pub fn bg(&self, _color: color::Color) { + pub fn bg(&self, _color: color::Color) -> bool { + false } pub fn reset(&self) { From 690495de035ccaf743b2715dd1e88b1266878026 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 10 Jul 2013 14:46:20 -0700 Subject: [PATCH 2/4] term: Add new function .attr() to toggle terminal attributes Also add .supports_attr() to test for attribute support without writing anything to output. Update .reset() to use sgr0 instead of op. --- src/libextra/term.rs | 113 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/src/libextra/term.rs b/src/libextra/term.rs index 1740f4d1ecc16..1cfb4f4afa627 100644 --- a/src/libextra/term.rs +++ b/src/libextra/term.rs @@ -45,6 +45,54 @@ pub mod color { pub static BRIGHT_WHITE: Color = 15u16; } +pub mod attr { + /// Terminal attributes for use with term.attr(). + /// Most attributes can only be turned on and must be turned off with term.reset(). + /// The ones that can be turned off explicitly take a boolean value. + /// Color is also represented as an attribute for convenience. + pub enum Attr { + /// Bold (or possibly bright) mode + Bold, + /// Dim mode, also called faint or half-bright. Often not supported + Dim, + /// Italics mode. Often not supported + Italic(bool), + /// Underline mode + Underline(bool), + /// Blink mode + Blink, + /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold + Standout(bool), + /// Reverse mode, inverts the foreground and background colors + Reverse, + /// Secure mode, also called invis mode. Hides the printed text + Secure, + /// Convenience attribute to set the foreground color + ForegroundColor(super::color::Color), + /// Convenience attribute to set the background color + BackgroundColor(super::color::Color) + } +} + +#[cfg(not(target_os = "win32"))] +priv fn cap_for_attr(attr: attr::Attr) -> &'static str { + match attr { + attr::Bold => "bold", + attr::Dim => "dim", + attr::Italic(true) => "sitm", + attr::Italic(false) => "ritm", + attr::Underline(true) => "smul", + attr::Underline(false) => "rmul", + attr::Blink => "blink", + attr::Standout(true) => "smso", + attr::Standout(false) => "rmso", + attr::Reverse => "rev", + attr::Secure => "invis", + attr::ForegroundColor(_) => "setaf", + attr::BackgroundColor(_) => "setab" + } +} + #[cfg(not(target_os = "win32"))] pub struct Terminal { num_colors: u16, @@ -124,17 +172,64 @@ impl Terminal { } false } + + /// Sets the given terminal attribute, if supported. + /// Returns true if the attribute was supported, false otherwise. + pub fn attr(&self, attr: attr::Attr) -> bool { + match attr { + 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", s.unwrap_err()); + } + } + false + } + } + } + + /// Returns whether the given terminal attribute is supported. + pub fn supports_attr(&self, attr: attr::Attr) -> bool { + match attr { + attr::ForegroundColor(_) | attr::BackgroundColor(_) => { + self.num_colors > 0 + } + _ => { + let cap = cap_for_attr(attr); + self.ti.strings.find_equiv(&cap).is_some() + } + } + } + + /// Resets all terminal attributes and color to the default. pub fn reset(&self) { - let mut vars = Variables::new(); - let s = do self.ti.strings.find_equiv(&("op")) - .map_consume_default(Err(~"can't find terminfo capability `op`")) |op| { - expand(copy *op, [], &mut vars) - }; + 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")); + } + } + let s = do cap.map_consume_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", 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", s.unwrap_err()); } } @@ -160,6 +255,14 @@ impl Terminal { false } + pub fn attr(&self, _attr: attr::Attr) -> bool { + false + } + + pub fn supports_attr(&self, _attr: attr::Attr) -> bool { + false + } + pub fn reset(&self) { } } From 69da3808444d71a39d3c05519a45d5186537cfcb Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 10 Jul 2013 23:35:13 -0700 Subject: [PATCH 3/4] Highlight rustc's warnings/errors in bold instead of bright white Clang actually highlights using bold, not using bright white. Match clang on this so our diagnostics are still readable on terminals with a white background. --- src/libsyntax/diagnostic.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 0180c2b31d723..fc0dc4c8f5230 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -186,22 +186,22 @@ fn diagnosticcolor(lvl: level) -> term::color::Color { } } -fn print_maybe_colored(msg: &str, color: term::color::Color) { +fn print_maybe_styled(msg: &str, color: term::attr::Attr) { let stderr = io::stderr(); - let t = term::Terminal::new(stderr); + if stderr.get_type() == io::Screen { + let t = term::Terminal::new(stderr); - match t { - Ok(term) => { - if stderr.get_type() == io::Screen { - term.fg(color); + match t { + Ok(term) => { + term.attr(color); stderr.write_str(msg); term.reset(); - } else { - stderr.write_str(msg); - } - }, - _ => stderr.write_str(msg) + }, + _ => stderr.write_str(msg) + } + } else { + stderr.write_str(msg); } } @@ -212,8 +212,9 @@ fn print_diagnostic(topic: &str, lvl: level, msg: &str) { stderr.write_str(fmt!("%s ", topic)); } - print_maybe_colored(fmt!("%s: ", diagnosticstr(lvl)), diagnosticcolor(lvl)); - print_maybe_colored(fmt!("%s\n", msg), term::color::BRIGHT_WHITE); + print_maybe_styled(fmt!("%s: ", diagnosticstr(lvl)), + term::attr::ForegroundColor(diagnosticcolor(lvl))); + print_maybe_styled(fmt!("%s\n", msg), term::attr::Bold); } pub fn collect(messages: @mut ~[~str]) @@ -312,7 +313,7 @@ fn highlight_lines(cm: @codemap::CodeMap, s.push_char('~') } } - print_maybe_colored(s + "\n", diagnosticcolor(lvl)); + print_maybe_styled(s + "\n", term::attr::ForegroundColor(diagnosticcolor(lvl))); } } From 1d4c3146f5e35ce60db73849da8806d73c6ecee2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 11 Jul 2013 00:56:26 -0700 Subject: [PATCH 4/4] Don't re-parse terminfo (twice!) on every compiler diagnostic Stuff the term::Terminal into TLS to avoid re-parsing for every single message we want to color. Fixes #6827. --- src/libsyntax/diagnostic.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index fc0dc4c8f5230..2971ad5cc2942 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -13,6 +13,7 @@ use codemap; use std::io; use std::uint; +use std::local_data; use extra::term; pub type Emitter = @fn(cmsp: Option<(@codemap::CodeMap, span)>, @@ -187,13 +188,29 @@ fn diagnosticcolor(lvl: level) -> term::color::Color { } fn print_maybe_styled(msg: &str, color: term::attr::Attr) { + #[cfg(not(stage0))] + static tls_terminal: local_data::Key<@Option> = &local_data::Key; + #[cfg(stage0)] + fn tls_terminal(_: @Option) {} + let stderr = io::stderr(); if stderr.get_type() == io::Screen { - let t = term::Terminal::new(stderr); + let t = match local_data::get(tls_terminal, |v| v.map_consume(|&k|k)) { + None => { + let t = term::Terminal::new(stderr); + let tls = @match t { + Ok(t) => Some(t), + Err(_) => None + }; + local_data::set(tls_terminal, tls); + &*tls + } + Some(tls) => &*tls + }; match t { - Ok(term) => { + &Some(ref term) => { term.attr(color); stderr.write_str(msg); term.reset();