summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Gallant <jamslam@gmail.com>2016-09-08 21:46:14 -0400
committerAndrew Gallant <jamslam@gmail.com>2016-09-08 21:46:14 -0400
commit0766617e07ecccdeef8647f783f81deab9d03eac (patch)
tree4d34b46a49537e535506f137b8b02dc714819ce7 /src
parentafd99c43d7aee8bd85e728b2c947ccdc2ecc3bc7 (diff)
Refactor how coloring is done.0.0.14
All in the name of appeasing Windows.
Diffstat (limited to 'src')
-rw-r--r--src/args.rs20
-rw-r--r--src/main.rs25
-rw-r--r--src/out.rs480
-rw-r--r--src/printer.rs330
-rw-r--r--src/search.rs12
-rw-r--r--src/search_buffer.rs10
6 files changed, 497 insertions, 380 deletions
diff --git a/src/args.rs b/src/args.rs
index 7b363d00..d6a9657f 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -9,11 +9,12 @@ use grep::{Grep, GrepBuilder};
use log;
use num_cpus;
use regex;
+use term::Terminal;
use walkdir::WalkDir;
use gitignore::{Gitignore, GitignoreBuilder};
use ignore::Ignore;
-use out::Out;
+use out::{Out, OutBuffer};
use printer::Printer;
use search::{InputBuffer, Searcher};
use search_buffer::BufferSearcher;
@@ -438,8 +439,8 @@ impl Args {
/// Create a new printer of individual search results that writes to the
/// writer given.
- pub fn printer<W: Send + io::Write>(&self, wtr: W) -> Printer<W> {
- let mut p = Printer::new(wtr, self.color)
+ pub fn printer<W: Send + Terminal>(&self, wtr: W) -> Printer<W> {
+ let mut p = Printer::new(wtr)
.column(self.column)
.context_separator(self.context_separator.clone())
.eol(self.eol)
@@ -454,8 +455,8 @@ impl Args {
/// Create a new printer of search results for an entire file that writes
/// to the writer given.
- pub fn out<W: io::Write>(&self, wtr: W) -> Out<W> {
- let mut out = Out::new(wtr);
+ pub fn out(&self) -> Out {
+ let mut out = Out::new(self.color);
if self.heading && !self.count {
out = out.file_separator(b"".to_vec());
} else if self.before_context > 0 || self.after_context > 0 {
@@ -464,6 +465,11 @@ impl Args {
out
}
+ /// Create a new buffer for use with searching.
+ pub fn outbuf(&self) -> OutBuffer {
+ OutBuffer::new(self.color)
+ }
+
/// Return the paths that should be searched.
pub fn paths(&self) -> &[PathBuf] {
&self.paths
@@ -472,7 +478,7 @@ impl Args {
/// Create a new line based searcher whose configuration is taken from the
/// command line. This searcher supports a dizzying array of features:
/// inverted matching, line counting, context control and more.
- pub fn searcher<'a, R: io::Read, W: Send + io::Write>(
+ pub fn searcher<'a, R: io::Read, W: Send + Terminal>(
&self,
inp: &'a mut InputBuffer,
printer: &'a mut Printer<W>,
@@ -493,7 +499,7 @@ impl Args {
/// Create a new line based searcher whose configuration is taken from the
/// command line. This search operates on an entire file all once (which
/// may have been memory mapped).
- pub fn searcher_buffer<'a, W: Send + io::Write>(
+ pub fn searcher_buffer<'a, W: Send + Terminal>(
&self,
printer: &'a mut Printer<W>,
grep: &'a Grep,
diff --git a/src/main.rs b/src/main.rs
index 722232a8..493256f0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -39,7 +39,7 @@ use term::Terminal;
use walkdir::DirEntry;
use args::Args;
-use out::Out;
+use out::{NoColorTerminal, Out, OutBuffer};
use printer::Printer;
use search::InputBuffer;
@@ -90,7 +90,8 @@ fn run(args: Args) -> Result<u64> {
return run_types(args);
}
let args = Arc::new(args);
- let out = Arc::new(Mutex::new(args.out(io::stdout())));
+ let out = Arc::new(Mutex::new(args.out()));
+ let outbuf = args.outbuf();
let mut workers = vec![];
let mut workq = {
@@ -101,7 +102,7 @@ fn run(args: Args) -> Result<u64> {
out: out.clone(),
chan_work: stealer.clone(),
inpbuf: args.input_buffer(),
- outbuf: Some(vec![]),
+ outbuf: Some(outbuf.clone()),
grep: args.grep(),
match_count: 0,
};
@@ -129,7 +130,8 @@ fn run(args: Args) -> Result<u64> {
}
fn run_files(args: Args) -> Result<u64> {
- let mut printer = args.printer(io::BufWriter::new(io::stdout()));
+ let term = NoColorTerminal::new(io::BufWriter::new(io::stdout()));
+ let mut printer = args.printer(term);
let mut file_count = 0;
for p in args.paths() {
if p == Path::new("-") {
@@ -146,7 +148,8 @@ fn run_files(args: Args) -> Result<u64> {
}
fn run_types(args: Args) -> Result<u64> {
- let mut printer = args.printer(io::BufWriter::new(io::stdout()));
+ let term = NoColorTerminal::new(io::BufWriter::new(io::stdout()));
+ let mut printer = args.printer(term);
let mut ty_count = 0;
for def in args.type_defs() {
printer.type_def(def);
@@ -168,10 +171,10 @@ enum WorkReady {
struct Worker {
args: Arc<Args>,
- out: Arc<Mutex<Out<io::Stdout>>>,
+ out: Arc<Mutex<Out>>,
chan_work: Stealer<Work>,
inpbuf: InputBuffer,
- outbuf: Option<Vec<u8>>,
+ outbuf: Option<OutBuffer>,
grep: Grep,
match_count: u64,
}
@@ -203,12 +206,12 @@ impl Worker {
let mut out = self.out.lock().unwrap();
out.write(&outbuf);
}
- self.outbuf = Some(outbuf.into_inner());
+ self.outbuf = Some(outbuf);
}
self.match_count
}
- fn do_work<W: Send + io::Write>(
+ fn do_work<W: Send + Terminal>(
&mut self,
printer: &mut Printer<W>,
work: WorkReady,
@@ -241,7 +244,7 @@ impl Worker {
}
}
- fn search<R: io::Read, W: Send + io::Write>(
+ fn search<R: io::Read, W: Send + Terminal>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
@@ -256,7 +259,7 @@ impl Worker {
).run().map_err(From::from)
}
- fn search_mmap<W: Send + io::Write>(
+ fn search_mmap<W: Send + Terminal>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
diff --git a/src/out.rs b/src/out.rs
index 9fb31a09..14999127 100644
--- a/src/out.rs
+++ b/src/out.rs
@@ -1,10 +1,40 @@
use std::io::{self, Write};
+use std::sync::Arc;
-use term::{StdoutTerminal, Terminal};
+use term::{self, Terminal};
+use term::color::Color;
+use term::terminfo::TermInfo;
#[cfg(windows)]
use term::WinConsole;
-use printer::Writer;
+use terminal::TerminfoTerminal;
+
+pub type StdoutTerminal = Box<Terminal<Output=io::BufWriter<io::Stdout>> + Send>;
+
+/// Gets a terminal that supports color if available.
+#[cfg(windows)]
+fn term_stdout(color: bool) -> StdoutTerminal {
+ let stdout = io::BufWriter::new(io::stdout());
+ WinConsole::new(stdout)
+ .ok()
+ .map(|t| Box::new(t))
+ .unwrap_or_else(|| {
+ let stdout = io::BufWriter::new(io::stdout());
+ Box::new(NoColorTerminal::new(stdout))
+ })
+}
+
+/// Gets a terminal that supports color if available.
+#[cfg(not(windows))]
+fn term_stdout(color: bool) -> StdoutTerminal {
+ let stdout = io::BufWriter::new(io::stdout());
+ if !color || TERMINFO.is_none() {
+ Box::new(NoColorTerminal::new(stdout))
+ } else {
+ let info = TERMINFO.clone().unwrap();
+ Box::new(TerminfoTerminal::new_with_terminfo(stdout, info))
+ }
+}
/// Out controls the actual output of all search results for a particular file
/// to the end user.
@@ -12,34 +42,17 @@ use printer::Writer;
/// (The difference between Out and Printer is that a Printer works with
/// individual search results where as Out works with search results for each
/// file as a whole. For example, it knows when to print a file separator.)
-pub struct Out<W: io::Write> {
- wtr: io::BufWriter<W>,
- term: Option<Box<StdoutTerminal>>,
+pub struct Out {
+ term: StdoutTerminal,
printed: bool,
file_separator: Option<Vec<u8>>,
}
-/// This is like term::stdout, but on Windows always uses WinConsole instead
-/// of trying for a TerminfoTerminal. This may be a mistake.
-#[cfg(windows)]
-fn term_stdout() -> Option<Box<StdoutTerminal>> {
- WinConsole::new(io::stdout())
- .ok()
- .map(|t| Box::new(t) as Box<StdoutTerminal>)
-}
-
-#[cfg(not(windows))]
-fn term_stdout() -> Option<Box<StdoutTerminal>> {
- // We never use this crap on *nix.
- None
-}
-
-impl<W: io::Write> Out<W> {
+impl Out {
/// Create a new Out that writes to the wtr given.
- pub fn new(wtr: W) -> Out<W> {
+ pub fn new(color: bool) -> Out {
Out {
- wtr: io::BufWriter::new(wtr),
- term: term_stdout(),
+ term: term_stdout(color),
printed: false,
file_separator: None,
}
@@ -49,39 +62,422 @@ impl<W: io::Write> Out<W> {
/// By default, no separator is printed.
///
/// If sep is empty, then no file separator is printed.
- pub fn file_separator(mut self, sep: Vec<u8>) -> Out<W> {
+ pub fn file_separator(mut self, sep: Vec<u8>) -> Out {
self.file_separator = Some(sep);
self
}
/// Write the search results of a single file to the underlying wtr and
/// flush wtr.
- pub fn write(&mut self, buf: &Writer<Vec<u8>>) {
+ pub fn write(&mut self, buf: &OutBuffer) {
if let Some(ref sep) = self.file_separator {
if self.printed {
- let _ = self.wtr.write_all(sep);
- let _ = self.wtr.write_all(b"\n");
+ let _ = self.term.write_all(sep);
+ let _ = self.term.write_all(b"\n");
}
}
match *buf {
- Writer::Colored(ref tt) => {
- let _ = self.wtr.write_all(tt.get_ref());
+ OutBuffer::Colored(ref tt) => {
+ let _ = self.term.write_all(tt.get_ref());
}
- Writer::Windows(ref w) => {
- match self.term {
- None => {
- let _ = self.wtr.write_all(w.get_ref());
- }
- Some(ref mut stdout) => {
- w.print_stdout(stdout);
- }
- }
+ OutBuffer::Windows(ref w) => {
+ w.print_stdout(&mut self.term);
}
- Writer::NoColor(ref buf) => {
- let _ = self.wtr.write_all(buf);
+ OutBuffer::NoColor(ref buf) => {
+ let _ = self.term.write_all(buf);
}
}
- let _ = self.wtr.flush();
+ let _ = self.term.flush();
self.printed = true;
}
}
+
+/// OutBuffer corresponds to the final output buffer for search results. All
+/// search results are written to a buffer and then a buffer is flushed to
+/// stdout only after the full search has completed.
+#[derive(Clone, Debug)]
+pub enum OutBuffer {
+ Colored(TerminfoTerminal<Vec<u8>>),
+ Windows(WindowsBuffer),
+ NoColor(Vec<u8>),
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsBuffer {
+ buf: Vec<u8>,
+ pos: usize,
+ colors: Vec<WindowsColor>,
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsColor {
+ pos: usize,
+ opt: WindowsOption,
+}
+
+#[derive(Clone, Debug)]
+pub enum WindowsOption {
+ Foreground(Color),
+ Background(Color),
+ Reset,
+}
+
+lazy_static! {
+ static ref TERMINFO: Option<Arc<TermInfo>> = {
+ match TermInfo::from_env() {
+ Ok(info) => Some(Arc::new(info)),
+ Err(err) => {
+ debug!("error loading terminfo for coloring: {}", err);
+ None
+ }
+ }
+ };
+}
+
+impl OutBuffer {
+ /// Create a new output buffer.
+ ///
+ /// When color is true, the buffer will attempt to support coloring.
+ pub fn new(color: bool) -> OutBuffer {
+ // If we want color, build a TerminfoTerminal and see if the current
+ // environment supports coloring. If not, bail with NoColor. To avoid
+ // losing our writer (ownership), do this the long way.
+ if !color {
+ return OutBuffer::NoColor(vec![]);
+ }
+ if cfg!(windows) {
+ return OutBuffer::Windows(WindowsBuffer {
+ buf: vec![],
+ pos: 0,
+ colors: vec![]
+ });
+ }
+ if TERMINFO.is_none() {
+ return OutBuffer::NoColor(vec![]);
+ }
+ let info = TERMINFO.clone().unwrap();
+ let tt = TerminfoTerminal::new_with_terminfo(vec![], info);
+ if !tt.supports_color() {
+ debug!("environment doesn't support coloring");
+ return OutBuffer::NoColor(tt.into_inner());
+ }
+ OutBuffer::Colored(tt)
+ }
+
+ /// Clear the give buffer of all search results such that it is reusable
+ /// in another search.
+ pub fn clear(&mut self) {
+ match *self {
+ OutBuffer::Colored(ref mut tt) => {
+ tt.get_mut().clear();
+ }
+ OutBuffer::Windows(ref mut win) => {
+ win.buf.clear();
+ win.colors.clear();
+ win.pos = 0;
+ }
+ OutBuffer::NoColor(ref mut buf) => {
+ buf.clear();
+ }
+ }
+ }
+
+ fn map_result<F, G>(
+ &mut self,
+ mut f: F,
+ mut g: G,
+ ) -> term::Result<()>
+ where F: FnMut(&mut TerminfoTerminal<Vec<u8>>) -> term::Result<()>,
+ G: FnMut(&mut WindowsBuffer) -> term::Result<()> {
+ match *self {
+ OutBuffer::Colored(ref mut w) => f(w),
+ OutBuffer::Windows(ref mut w) => g(w),
+ OutBuffer::NoColor(_) => Err(term::Error::NotSupported),
+ }
+ }
+
+ fn map_bool<F, G>(
+ &self,
+ mut f: F,
+ mut g: G,
+ ) -> bool
+ where F: FnMut(&TerminfoTerminal<Vec<u8>>) -> bool,
+ G: FnMut(&WindowsBuffer) -> bool {
+ match *self {
+ OutBuffer::Colored(ref w) => f(w),
+ OutBuffer::Windows(ref w) => g(w),
+ OutBuffer::NoColor(_) => false,
+ }
+ }
+}
+
+impl io::Write for OutBuffer {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match *self {
+ OutBuffer::Colored(ref mut w) => w.write(buf),
+ OutBuffer::Windows(ref mut w) => w.write(buf),
+ OutBuffer::NoColor(ref mut w) => w.write(buf),
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl term::Terminal for OutBuffer {
+ type Output = Vec<u8>;
+
+ fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
+ self.map_result(|w| w.fg(fg), |w| w.fg(fg))
+ }
+
+ fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
+ self.map_result(|w| w.bg(bg), |w| w.bg(bg))
+ }
+
+ fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
+ self.map_result(|w| w.attr(attr), |w| w.attr(attr))
+ }
+
+ fn supports_attr(&self, attr: term::Attr) -> bool {
+ self.map_bool(|w| w.supports_attr(attr), |w| w.supports_attr(attr))
+ }
+
+ fn reset(&mut self) -> term::Result<()> {
+ self.map_result(|w| w.reset(), |w| w.reset())
+ }
+
+ fn supports_reset(&self) -> bool {
+ self.map_bool(|w| w.supports_reset(), |w| w.supports_reset())
+ }
+
+ fn supports_color(&self) -> bool {
+ self.map_bool(|w| w.supports_color(), |w| w.supports_color())
+ }
+
+ fn cursor_up(&mut self) -> term::Result<()> {
+ self.map_result(|w| w.cursor_up(), |w| w.cursor_up())
+ }
+
+ fn delete_line(&mut self) -> term::Result<()> {
+ self.map_result(|w| w.delete_line(), |w| w.delete_line())
+ }
+
+ fn carriage_return(&mut self) -> term::Result<()> {
+ self.map_result(|w| w.carriage_return(), |w| w.carriage_return())
+ }
+
+ fn get_ref(&self) -> &Vec<u8> {
+ match *self {
+ OutBuffer::Colored(ref w) => w.get_ref(),
+ OutBuffer::Windows(ref w) => w.get_ref(),
+ OutBuffer::NoColor(ref w) => w,
+ }
+ }
+
+ fn get_mut(&mut self) -> &mut Vec<u8> {
+ match *self {
+ OutBuffer::Colored(ref mut w) => w.get_mut(),
+ OutBuffer::Windows(ref mut w) => w.get_mut(),
+ OutBuffer::NoColor(ref mut w) => w,
+ }
+ }
+
+ fn into_inner(self) -> Vec<u8> {
+ match self {
+ OutBuffer::Colored(w) => w.into_inner(),
+ OutBuffer::Windows(w) => w.into_inner(),
+ OutBuffer::NoColor(w) => w,
+ }
+ }
+}
+
+impl WindowsBuffer {
+ fn push(&mut self, opt: WindowsOption) {
+ let pos = self.pos;
+ self.colors.push(WindowsColor { pos: pos, opt: opt });
+ }
+}
+
+impl WindowsBuffer {
+ /// Print the contents to the given terminal.
+ pub fn print_stdout(&self, tt: &mut StdoutTerminal) {
+ if !tt.supports_color() {
+ let _ = tt.write_all(&self.buf);
+ let _ = tt.flush();
+ return;
+ }
+ let mut last = 0;
+ for col in &self.colors {
+ let _ = tt.write_all(&self.buf[last..col.pos]);
+ match col.opt {
+ WindowsOption::Foreground(c) => {
+ let _ = tt.fg(c);
+ }
+ WindowsOption::Background(c) => {
+ let _ = tt.bg(c);
+ }
+ WindowsOption::Reset => {
+ let _ = tt.reset();
+ }
+ }
+ last = col.pos;
+ }
+ let _ = tt.write_all(&self.buf[last..]);
+ let _ = tt.flush();
+ }
+}
+
+impl io::Write for WindowsBuffer {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let n = try!(self.buf.write(buf));
+ self.pos += n;
+ Ok(n)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl term::Terminal for WindowsBuffer {
+ type Output = Vec<u8>;
+
+ fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
+ self.push(WindowsOption::Foreground(fg));
+ Ok(())
+ }
+
+ fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
+ self.push(WindowsOption::Background(bg));
+ Ok(())
+ }
+
+ fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn supports_attr(&self, attr: term::Attr) -> bool {
+ false
+ }
+
+ fn reset(&mut self) -> term::Result<()> {
+ self.push(WindowsOption::Reset);
+ Ok(())
+ }
+
+ fn supports_reset(&self) -> bool {
+ true
+ }
+
+ fn supports_color(&self) -> bool {
+ true
+ }
+
+ fn cursor_up(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn delete_line(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn carriage_return(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn get_ref(&self) -> &Vec<u8> {
+ &self.buf
+ }
+
+ fn get_mut(&mut self) -> &mut Vec<u8> {
+ &mut self.buf
+ }
+
+ fn into_inner(self) -> Vec<u8> {
+ self.buf
+ }
+}
+
+/// NoColorTerminal implements Terminal, but supports no coloring.
+///
+/// Its useful when an API requires a Terminal, but coloring isn't needed.
+pub struct NoColorTerminal<W> {
+ wtr: W,
+}
+
+impl<W: Send + io::Write> NoColorTerminal<W> {
+ /// Wrap the given writer in a Terminal interface.
+ pub fn new(wtr: W) -> NoColorTerminal<W> {
+ NoColorTerminal {
+ wtr: wtr,
+ }
+ }
+}
+
+impl<W: Send + io::Write> io::Write for NoColorTerminal<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.wtr.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.wtr.flush()
+ }
+}
+
+impl<W: Send + io::Write> term::Terminal for NoColorTerminal<W> {
+ type Output = W;
+
+ fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn supports_attr(&self, attr: term::Attr) -> bool {
+ false
+ }
+
+ fn reset(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn supports_reset(&self) -> bool {
+ false
+ }
+
+ fn supports_color(&self) -> bool {
+ false
+ }
+
+ fn cursor_up(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn delete_line(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn carriage_return(&mut self) -> term::Result<()> {
+ Err(term::Error::NotSupported)
+ }
+
+ fn get_ref(&self) -> &W {
+ &self.wtr
+ }
+
+ fn get_mut(&mut self) -> &mut W {
+ &mut self.wtr
+ }
+
+ fn into_inner(self) -> W {
+ self.wtr
+ }
+}
diff --git a/src/printer.rs b/src/printer.rs
index b82e0fec..b5070b88 100644
--- a/src/printer.rs
+++ b/src/printer.rs
@@ -1,17 +1,11 @@
-use std::io::{self, Write};
use std::path::Path;
-use std::sync::Arc;
use regex::bytes::Regex;
-use term::{self, StdoutTerminal, Terminal};
-use term::color::*;
-use term::terminfo::TermInfo;
+use term::{Attr, Terminal};
+use term::color;
-use terminal::TerminfoTerminal;
use types::FileTypeDef;
-use self::Writer::*;
-
/// Printer encapsulates all output logic for searching.
///
/// Note that we currently ignore all write errors. It's probably worthwhile
@@ -19,7 +13,7 @@ use self::Writer::*;
/// writes to memory, neither of which commonly fail.
pub struct Printer<W> {
/// The underlying writer.
- wtr: Writer<W>,
+ wtr: W,
/// Whether anything has been printed to wtr yet.
has_printed: bool,
/// Whether to show column numbers for the first match or not.
@@ -42,13 +36,11 @@ pub struct Printer<W> {
with_filename: bool,
}
-impl<W: Send + io::Write> Printer<W> {
+impl<W: Send + Terminal> Printer<W> {
/// Create a new printer that writes to wtr.
- ///
- /// `color` should be true if the printer should try to use coloring.
- pub fn new(wtr: W, color: bool) -> Printer<W> {
+ pub fn new(wtr: W) -> Printer<W> {
Printer {
- wtr: Writer::new(wtr, color),
+ wtr: wtr,
has_printed: false,
column: false,
context_separator: "--".to_string().into_bytes(),
@@ -115,7 +107,7 @@ impl<W: Send + io::Write> Printer<W> {
}
/// Flushes the underlying writer and returns it.
- pub fn into_inner(mut self) -> Writer<W> {
+ pub fn into_inner(mut self) -> W {
let _ = self.wtr.flush();
self.wtr
}
@@ -201,15 +193,15 @@ impl<W: Send + io::Write> Printer<W> {
}
pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
- if !self.wtr.is_color() {
+ if !self.wtr.supports_color() {
self.write(buf);
return;
}
let mut last_written = 0;
for (s, e) in re.find_iter(buf) {
self.write(&buf[last_written..s]);
- let _ = self.wtr.fg(BRIGHT_RED);
- let _ = self.wtr.attr(term::Attr::Bold);
+ let _ = self.wtr.fg(color::BRIGHT_RED);
+ let _ = self.wtr.attr(Attr::Bold);
self.write(&buf[s..e]);
let _ = self.wtr.reset();
last_written = e;
@@ -241,24 +233,24 @@ impl<W: Send + io::Write> Printer<W> {
}
fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
- if self.wtr.is_color() {
- let _ = self.wtr.fg(BRIGHT_GREEN);
- let _ = self.wtr.attr(term::Attr::Bold);
+ if self.wtr.supports_color() {
+ let _ = self.wtr.fg(color::BRIGHT_GREEN);
+ let _ = self.wtr.attr(Attr::Bold);
}
self.write(path.as_ref().to_string_lossy().as_bytes());
self.write_eol();
- if self.wtr.is_color() {
+ if self.wtr.supports_color() {
let _ = self.wtr.reset();
}
}
fn line_number(&mut self, n: u64, sep: u8) {
- if self.wtr.is_color() {
- let _ = self.wtr.fg(BRIGHT_BLUE);
- let _ = self.wtr.attr(term::Attr::Bold);
+ if self.wtr.supports_color() {
+ let _ = self.wtr.fg(color::BRIGHT_BLUE);
+ let _ = self.wtr.attr(Attr::Bold);
}
self.write(n.to_string().as_bytes());
- if self.wtr.is_color() {
+ if self.wtr.supports_color() {
let _ = self.wtr.reset();
}
self.write(&[sep]);
@@ -277,289 +269,3 @@ impl<W: Send + io::Write> Printer<W> {
self.write(&[eol]);
}
}
-
-/// Writer corresponds to the final output buffer for search results. All
-/// search results are written to a Writer and then a Writer is flushed to
-/// stdout only after the full search has completed.
-pub enum Writer<W> {
- Colored(TerminfoTerminal<W>),
- Windows(WindowsWriter<W>),
- NoColor(W),
-}
-
-pub struct WindowsWriter<W> {
- wtr: W,
- pos: usize,
- colors: Vec<WindowsColor>,
-}
-
-pub struct WindowsColor {
- pos: usize,
- opt: WindowsOption,
-}
-
-pub enum WindowsOption {
- Foreground(Color),
- Background(Color),
- Reset,
-}
-
-lazy_static! {
- static ref TERMINFO: Option<Arc<TermInfo>> = {
- match term::terminfo::TermInfo::from_env() {
- Ok(info) => Some(Arc::new(info)),
- Err(err) => {
- debug!("error loading terminfo for coloring: {}", err);
- None
- }
- }
- };
-}
-
-impl<W: Send + io::Write> Writer<W> {
- fn new(wtr: W, color: bool) -> Writer<W> {
- // If we want color, build a TerminfoTerminal and see if the current
- // environment supports coloring. If not, bail with NoColor. To avoid
- // losing our writer (ownership), do this the long way.
- if !color {
- return NoColor(wtr);
- }
- if cfg!(windows) {
- return Windows(WindowsWriter { wtr: wtr, pos: 0, colors: vec![] });
- }
- if TERMINFO.is_none() {
- return NoColor(wtr);
- }
- let info = TERMINFO.clone().unwrap();
- let tt = TerminfoTerminal::new_with_terminfo(wtr, info);
- if !tt.supports_color() {
- debug!("environment doesn't support coloring");
- return NoColor(tt.into_inner());
- }
- Colored(tt)
- }
-
- fn is_color(&self) -> bool {
- match *self {
- Colored(_) => true,
- Windows(_) => true,
- NoColor(_) => false,
- }
- }
-
- fn map_result<F, G>(
- &mut self,
- mut f: F,
- mut g: G,
- ) -> term::Result<()>
- where F: FnMut(&mut TerminfoTerminal<W>) -> term::Result<()>,
- G: FnMut(&mut WindowsWriter<W>) -> term::Result<()> {
- match *self {
- Colored(ref mut w) => f(w),
- Windows(ref mut w) => g(w),
- NoColor(_) => Err(term::Error::NotSupported),
- }
- }
-
- fn map_bool<F, G>(
- &self,
- mut f: F,
- mut g: G,
- ) -> bool
- where F: FnMut(&TerminfoTerminal<W>) -> bool,
- G: FnMut(&WindowsWriter<W>) -> bool {
- match *self {
- Colored(ref w) => f(w),
- Windows(ref w) => g(w),
- NoColor(_) => false,
- }
- }
-}
-
-impl<W: Send + io::Write> io::Write for Writer<W> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- match *self {
- Colored(ref mut w) => w.write(buf),
- Windows(ref mut w) => w.write(buf),
- NoColor(ref mut w) => w.write(buf),
- }
- }
-
- fn flush(&mut self) -> io::Result<()> {
- match *self {
- Colored(ref mut w) => w.flush(),
- Windows(ref mut w) => w.flush(),
- NoColor(ref mut w) => w.flush(),
- }
- }
-}
-
-impl<W: Send + io::Write> term::Terminal for Writer<W> {
- type Output = W;
-
- fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
- self.map_result(|w| w.fg(fg), |w| w.fg(fg))
- }
-
- fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
- self.map_result(|w| w.bg(bg), |w| w.bg(bg))
- }
-
- fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
- self.map_result(|w| w.attr(attr), |w| w.attr(attr))
- }
-
- fn supports_attr(&self, attr: term::Attr) -> bool {
- self.map_bool(|w| w.supports_attr(attr), |w| w.supports_attr(attr))
- }
-
- fn reset(&mut self) -> term::Result<()> {
- self.map_result(|w| w.reset(), |w| w.reset())
- }
-
- fn supports_reset(&self) -> bool {
- self.map_bool(|w| w.supports_reset(), |w| w.supports_reset())
- }
-
- fn supports_color(&self) -> bool {
- self.map_bool(|w| w.supports_color(), |w| w.supports_color())
- }
-
- fn cursor_up(&mut self) -> term::Result<()> {
- self.map_result(|w| w.cursor_up(), |w| w.cursor_up())
- }
-
- fn delete_line(&mut self) -> term::Result<()> {
- self.map_result(|w| w.delete_line(), |w| w.delete_line())
- }
-
- fn carriage_return(&mut self) -> term::Result<()> {
- self.map_result(|w| w.carriage_return(), |w| w.carriage_return())
- }
-
- fn get_ref(&self) -> &W {
- match *self {
- Colored(ref w) => w.get_ref(),
- Windows(ref w) => w.get_ref(),
- NoColor(ref w) => w,
- }
- }
-
- fn get_mut(&mut self) -> &mut W {
- match *self {
- Colored(ref mut w) => w.get_mut(),
- Windows(ref mut w) => w.get_mut(),
- NoColor(ref mut w) => w,
- }
- }
-
- fn into_inner(self) -> W {
- match self {
- Colored(w) => w.into_inner(),
- Windows(w) => w.into_inner(),
- NoColor(w) => w,
- }
- }
-}
-
-impl<W: Send + io::Write> WindowsWriter<W> {
- fn push(&mut self, opt: WindowsOption) {
- let pos = self.pos;
- self.colors.push(WindowsColor { pos: pos, opt: opt });
- }
-}
-
-impl WindowsWriter<Vec<u8>> {
- /// Print the contents to the given terminal.
- pub fn print_stdout(&self, tt: &mut Box<StdoutTerminal>) {
- let mut last = 0;
- for col in &self.colors {
- let _ = tt.write_all(&self.wtr[last..col.pos]);
- match col.opt {
- WindowsOption::Foreground(c) => {
- let _ = tt.fg(c);
- }
- WindowsOption::Background(c) => {
- let _ = tt.bg(c);
- }
- WindowsOption::Reset => {
- let _ = tt.reset();
- }
- }
- last = col.pos;
- }
- let _ = tt.write_all(&self.wtr[last..]);
- let _ = tt.flush();
- }
-}
-
-impl<W: Send + io::Write> io::Write for WindowsWriter<W> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- let n = try!(self.wtr.write(buf