提交 0766617e 编写于 作者: A Andrew Gallant

Refactor how coloring is done.

All in the name of appeasing Windows.
上级 afd99c43
......@@ -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,
......
......@@ -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,
......
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
}
}
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));
self.pos += n;
Ok(n)
}
fn flush(&mut self) -> io::Result<()> {
self.wtr.flush()
}
}
impl<W: Send + io::Write> term::Terminal for WindowsWriter<W> {
type Output = W;
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) -> &W {
&self.wtr
}
fn get_mut(&mut self) -> &mut W {
&mut self.wtr
}
fn into_inner(self) -> W {
self.wtr
}
}
......@@ -11,6 +11,7 @@ use std::path::{Path, PathBuf};
use grep::{Grep, Match};
use memchr::{memchr, memrchr};
use term::Terminal;
use printer::Printer;
......@@ -98,7 +99,7 @@ impl Default for Options {
}
}
impl<'a, R: io::Read, W: Send + io::Write> Searcher<'a, R, W> {
impl<'a, R: io::Read, W: Send + Terminal> Searcher<'a, R, W> {
/// Create a new searcher.
///
/// `inp` is a reusable input buffer that is used as scratch space by this
......@@ -689,6 +690,7 @@ mod tests {
use grep::{Grep, GrepBuilder};
use term::Terminal;
use out::OutBuffer;
use printer::Printer;
use super::{InputBuffer, Searcher, start_of_previous_lines};
......@@ -731,7 +733,7 @@ fn main() {
&Path::new("/baz.rs")
}
type TestSearcher<'a> = Searcher<'a, io::Cursor<Vec<u8>>, Vec<u8>>;
type TestSearcher<'a> = Searcher<'a, io::Cursor<Vec<u8>>, OutBuffer>;
fn search_smallcap<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str,
......@@ -739,7 +741,8 @@ fn main() {
mut map: F,
) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![], false).with_filename(true);
let outbuf = OutBuffer::NoColor(vec![]);
let mut pp = Printer::new(outbuf).with_filename(true);
let grep = GrepBuilder::new(pat).build().unwrap();
let count = {
let searcher = Searcher::new(
......@@ -755,7 +758,8 @@ fn main() {
mut map: F,
) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(4096);
let mut pp = Printer::new(vec![], false).with_filename(true);
let outbuf = OutBuffer::NoColor(vec![]);
let mut pp = Printer::new(outbuf).with_filename(true);
let grep = GrepBuilder::new(pat).build().unwrap();
let count = {
let searcher = Searcher::new(
......
use std::cmp;
use std::io;
use std::path::Path;
use grep::Grep;
use term::Terminal;
use printer::Printer;
use search::{IterLines, Options, count_lines, is_binary};
......@@ -18,7 +18,7 @@ pub struct BufferSearcher<'a, W: 'a> {
last_line: usize,
}
impl<'a, W: Send + io::Write> BufferSearcher<'a, W> {
impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
pub fn new(
printer: &'a mut Printer<W>,
grep: &'a Grep,
......@@ -146,6 +146,7 @@ mod tests {
use grep::{Grep, GrepBuilder};
use term::Terminal;
use out::OutBuffer;
use printer::Printer;
use super::BufferSearcher;
......@@ -184,14 +185,15 @@ fn main() {
&Path::new("/baz.rs")
}
type TestSearcher<'a> = BufferSearcher<'a, Vec<u8>>;
type TestSearcher<'a> = BufferSearcher<'a, OutBuffer>;
fn search<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str,
haystack: &str,
mut map: F,
) -> (u64, String) {
let mut pp = Printer::new(vec![], false).with_filename(true);
let outbuf = OutBuffer::NoColor(vec![]);
let mut pp = Printer::new(outbuf).with_filename(true);
let grep = GrepBuilder::new(pat).build().unwrap();
let count = {
let searcher = BufferSearcher::new(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册