about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-07-14 15:55:20 -0700
committerbors <bors@rust-lang.org>2013-07-14 15:55:20 -0700
commit68a32aad1ab60b29ebfffd7c2f8d5967371955fc (patch)
tree62ece0bc2d2668d51ea96e1a4384a6ece703d831
parent4f333023e9d2d0b7a8d2c62df836158c880845b4 (diff)
parent1d4c3146f5e35ce60db73849da8806d73c6ecee2 (diff)
downloadrust-68a32aad1ab60b29ebfffd7c2f8d5967371955fc.tar.gz
rust-68a32aad1ab60b29ebfffd7c2f8d5967371955fc.zip
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).
-rw-r--r--src/libextra/term.rs131
-rw-r--r--src/libsyntax/diagnostic.rs46
2 files changed, 154 insertions, 23 deletions
diff --git a/src/libextra/term.rs b/src/libextra/term.rs
index cd226e2ad32..1cfb4f4afa6 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,
@@ -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) -> bool {
+        false
+    }
+
+    pub fn attr(&self, _attr: attr::Attr) -> bool {
+        false
     }
 
-    pub fn bg(&self, _color: color::Color) {
+    pub fn supports_attr(&self, _attr: attr::Attr) -> bool {
+        false
     }
 
     pub fn reset(&self) {
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 0180c2b31d7..2971ad5cc29 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)>,
@@ -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)));
     }
 }