auto merge of #7716 : kballard/rust/term-attr, r=cmr

Teach `extra::term` to support more terminal attributes than just color.

Fix the compiler diagnostic messages to print in bold instead of bright white. This matches Clang's output.

Cache the term::Terminal instead of re-parsing for every diagnostic (fixes #6827).
This commit is contained in:
bors 2013-07-14 15:55:20 -07:00
commit 68a32aad1a
2 changed files with 154 additions and 23 deletions

View File

@ -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,
@ -88,45 +136,100 @@ 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
}
/// 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());
}
}
@ -144,10 +247,20 @@ 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 attr(&self, _attr: attr::Attr) -> bool {
false
}
pub fn supports_attr(&self, _attr: attr::Attr) -> bool {
false
}
pub fn reset(&self) {

View File

@ -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)>,
@ -186,22 +187,38 @@ 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) {
#[cfg(not(stage0))]
static tls_terminal: local_data::Key<@Option<term::Terminal>> = &local_data::Key;
#[cfg(stage0)]
fn tls_terminal(_: @Option<term::Terminal>) {}
let stderr = io::stderr();
let t = term::Terminal::new(stderr);
if stderr.get_type() == io::Screen {
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) => {
if stderr.get_type() == io::Screen {
term.fg(color);
match t {
&Some(ref 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 +229,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 +330,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)));
}
}