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:
commit
68a32aad1a
@ -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) {
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user