]> git.lizzy.rs Git - rust.git/blob - src/libextra/term.rs
Forbid `priv` where it has no effect
[rust.git] / src / libextra / term.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Simple ANSI color library
12
13 #[allow(missing_doc)];
14
15
16 use std::io;
17
18 #[cfg(not(target_os = "win32"))] use std::os;
19 #[cfg(not(target_os = "win32"))] use terminfo::*;
20 #[cfg(not(target_os = "win32"))] use terminfo::searcher::open;
21 #[cfg(not(target_os = "win32"))] use terminfo::parser::compiled::parse;
22 #[cfg(not(target_os = "win32"))] use terminfo::parm::{expand, Number, Variables};
23
24 // FIXME (#2807): Windows support.
25
26 pub mod color {
27     pub type Color = u16;
28
29     pub static BLACK:   Color = 0u16;
30     pub static RED:     Color = 1u16;
31     pub static GREEN:   Color = 2u16;
32     pub static YELLOW:  Color = 3u16;
33     pub static BLUE:    Color = 4u16;
34     pub static MAGENTA: Color = 5u16;
35     pub static CYAN:    Color = 6u16;
36     pub static WHITE:   Color = 7u16;
37
38     pub static BRIGHT_BLACK:   Color = 8u16;
39     pub static BRIGHT_RED:     Color = 9u16;
40     pub static BRIGHT_GREEN:   Color = 10u16;
41     pub static BRIGHT_YELLOW:  Color = 11u16;
42     pub static BRIGHT_BLUE:    Color = 12u16;
43     pub static BRIGHT_MAGENTA: Color = 13u16;
44     pub static BRIGHT_CYAN:    Color = 14u16;
45     pub static BRIGHT_WHITE:   Color = 15u16;
46 }
47
48 pub mod attr {
49     /// Terminal attributes for use with term.attr().
50     /// Most attributes can only be turned on and must be turned off with term.reset().
51     /// The ones that can be turned off explicitly take a boolean value.
52     /// Color is also represented as an attribute for convenience.
53     pub enum Attr {
54         /// Bold (or possibly bright) mode
55         Bold,
56         /// Dim mode, also called faint or half-bright. Often not supported
57         Dim,
58         /// Italics mode. Often not supported
59         Italic(bool),
60         /// Underline mode
61         Underline(bool),
62         /// Blink mode
63         Blink,
64         /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
65         Standout(bool),
66         /// Reverse mode, inverts the foreground and background colors
67         Reverse,
68         /// Secure mode, also called invis mode. Hides the printed text
69         Secure,
70         /// Convenience attribute to set the foreground color
71         ForegroundColor(super::color::Color),
72         /// Convenience attribute to set the background color
73         BackgroundColor(super::color::Color)
74     }
75 }
76
77 #[cfg(not(target_os = "win32"))]
78 fn cap_for_attr(attr: attr::Attr) -> &'static str {
79     match attr {
80         attr::Bold               => "bold",
81         attr::Dim                => "dim",
82         attr::Italic(true)       => "sitm",
83         attr::Italic(false)      => "ritm",
84         attr::Underline(true)    => "smul",
85         attr::Underline(false)   => "rmul",
86         attr::Blink              => "blink",
87         attr::Standout(true)     => "smso",
88         attr::Standout(false)    => "rmso",
89         attr::Reverse            => "rev",
90         attr::Secure             => "invis",
91         attr::ForegroundColor(_) => "setaf",
92         attr::BackgroundColor(_) => "setab"
93     }
94 }
95
96 #[cfg(not(target_os = "win32"))]
97 pub struct Terminal {
98     num_colors: u16,
99     priv out: @io::Writer,
100     priv ti: ~TermInfo
101 }
102
103 #[cfg(target_os = "win32")]
104 pub struct Terminal {
105     num_colors: u16,
106     priv out: @io::Writer,
107 }
108
109 #[cfg(not(target_os = "win32"))]
110 impl Terminal {
111     pub fn new(out: @io::Writer) -> Result<Terminal, ~str> {
112         let term = os::getenv("TERM");
113         if term.is_none() {
114             return Err(~"TERM environment variable undefined");
115         }
116
117         let entry = open(term.unwrap());
118         if entry.is_err() {
119             return Err(entry.unwrap_err());
120         }
121
122         let ti = parse(entry.unwrap(), false);
123         if ti.is_err() {
124             return Err(ti.unwrap_err());
125         }
126
127         let inf = ti.unwrap();
128         let nc = if inf.strings.find_equiv(&("setaf")).is_some()
129                  && inf.strings.find_equiv(&("setab")).is_some() {
130                      inf.numbers.find_equiv(&("colors")).map_move_default(0, |&n| n)
131                  } else { 0 };
132
133         return Ok(Terminal {out: out, ti: inf, num_colors: nc});
134     }
135     /// Sets the foreground color to the given color.
136     ///
137     /// If the color is a bright color, but the terminal only supports 8 colors,
138     /// the corresponding normal color will be used instead.
139     ///
140     /// Returns true if the color was set, false otherwise.
141     pub fn fg(&self, color: color::Color) -> bool {
142         let color = self.dim_if_necessary(color);
143         if self.num_colors > color {
144             let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
145                            [Number(color as int)], &mut Variables::new());
146             if s.is_ok() {
147                 self.out.write(s.unwrap());
148                 return true
149             } else {
150                 warn!("%s", s.unwrap_err());
151             }
152         }
153         false
154     }
155     /// Sets the background color to the given color.
156     ///
157     /// If the color is a bright color, but the terminal only supports 8 colors,
158     /// the corresponding normal color will be used instead.
159     ///
160     /// Rturns true if the color was set, false otherwise.
161     pub fn bg(&self, color: color::Color) -> bool {
162         let color = self.dim_if_necessary(color);
163         if self.num_colors > color {
164             let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),
165                            [Number(color as int)], &mut Variables::new());
166             if s.is_ok() {
167                 self.out.write(s.unwrap());
168                 return true
169             } else {
170                 warn!("%s", s.unwrap_err());
171             }
172         }
173         false
174     }
175
176     /// Sets the given terminal attribute, if supported.
177     /// Returns true if the attribute was supported, false otherwise.
178     pub fn attr(&self, attr: attr::Attr) -> bool {
179         match attr {
180             attr::ForegroundColor(c) => self.fg(c),
181             attr::BackgroundColor(c) => self.bg(c),
182             _ => {
183                 let cap = cap_for_attr(attr);
184                 let parm = self.ti.strings.find_equiv(&cap);
185                 if parm.is_some() {
186                     let s = expand(*parm.unwrap(), [], &mut Variables::new());
187                     if s.is_ok() {
188                         self.out.write(s.unwrap());
189                         return true
190                     } else {
191                         warn!("%s", s.unwrap_err());
192                     }
193                 }
194                 false
195             }
196         }
197     }
198
199     /// Returns whether the given terminal attribute is supported.
200     pub fn supports_attr(&self, attr: attr::Attr) -> bool {
201         match attr {
202             attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
203                 self.num_colors > 0
204             }
205             _ => {
206                 let cap = cap_for_attr(attr);
207                 self.ti.strings.find_equiv(&cap).is_some()
208             }
209         }
210     }
211
212     /// Resets all terminal attributes and color to the default.
213     pub fn reset(&self) {
214         let mut cap = self.ti.strings.find_equiv(&("sgr0"));
215         if cap.is_none() {
216             // are there any terminals that have color/attrs and not sgr0?
217             // Try falling back to sgr, then op
218             cap = self.ti.strings.find_equiv(&("sgr"));
219             if cap.is_none() {
220                 cap = self.ti.strings.find_equiv(&("op"));
221             }
222         }
223         let s = do cap.map_move_default(Err(~"can't find terminfo capability `sgr0`")) |op| {
224             expand(*op, [], &mut Variables::new())
225         };
226         if s.is_ok() {
227             self.out.write(s.unwrap());
228         } else if self.num_colors > 0 {
229             warn!("%s", s.unwrap_err());
230         } else {
231             // if we support attributes but not color, it would be nice to still warn!()
232             // but it's not worth testing all known attributes just for this.
233             debug!("%s", s.unwrap_err());
234         }
235     }
236
237     fn dim_if_necessary(&self, color: color::Color) -> color::Color {
238         if color >= self.num_colors && color >= 8 && color < 16 {
239             color-8
240         } else { color }
241     }
242 }
243
244 #[cfg(target_os = "win32")]
245 impl Terminal {
246     pub fn new(out: @io::Writer) -> Result<Terminal, ~str> {
247         return Ok(Terminal {out: out, num_colors: 0});
248     }
249
250     pub fn fg(&self, _color: color::Color) -> bool {
251         false
252     }
253
254     pub fn bg(&self, _color: color::Color) -> bool {
255         false
256     }
257
258     pub fn attr(&self, _attr: attr::Attr) -> bool {
259         false
260     }
261
262     pub fn supports_attr(&self, _attr: attr::Attr) -> bool {
263         false
264     }
265
266     pub fn reset(&self) {
267     }
268 }