Compare commits

..

6 Commits

Author SHA1 Message Date
Andrew Gallant
cc6b6dcf5b fix windows build 2016-09-09 08:53:10 -04:00
Andrew Gallant
48878bbb8f update project name 2016-09-08 21:47:49 -04:00
Andrew Gallant
0766617e07 Refactor how coloring is done.
All in the name of appeasing Windows.
2016-09-08 21:46:14 -04:00
Andrew Gallant
afd99c43d7 fix deploy 2016-09-08 16:35:48 -04:00
Andrew Gallant
96e87ab738 update distributable to include readme and license 2016-09-08 16:21:37 -04:00
Andrew Gallant
a744ec133d Rename xrep to ripgrep. 2016-09-08 16:15:44 -04:00
15 changed files with 540 additions and 424 deletions

View File

@@ -16,7 +16,7 @@ cache: cargo
env: env:
global: global:
- PROJECT_NAME=xrep - PROJECT_NAME=ripgrep
matrix: matrix:
include: include:
# Nightly channel # Nightly channel

View File

@@ -1,14 +1,14 @@
[package] [package]
publish = false publish = false
name = "xrep" name = "ripgrep"
version = "0.1.0" #:version version = "0.1.0" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Line oriented search tool using Rust's regex library. Line oriented search tool using Rust's regex library.
""" """
documentation = "https://github.com/BurntSushi/xrep" documentation = "https://github.com/BurntSushi/ripgrep"
homepage = "https://github.com/BurntSushi/xrep" homepage = "https://github.com/BurntSushi/ripgrep"
repository = "https://github.com/BurntSushi/xrep" repository = "https://github.com/BurntSushi/ripgrep"
readme = "README.md" readme = "README.md"
keywords = ["regex", "grep", "egrep", "search", "pattern"] keywords = ["regex", "grep", "egrep", "search", "pattern"]
license = "Unlicense/MIT" license = "Unlicense/MIT"
@@ -16,7 +16,7 @@ license = "Unlicense/MIT"
[[bin]] [[bin]]
bench = false bench = false
path = "src/main.rs" path = "src/main.rs"
name = "xrep" name = "rg"
[dependencies] [dependencies]
crossbeam = "0.2" crossbeam = "0.2"

View File

@@ -1,6 +1,6 @@
environment: environment:
global: global:
PROJECT_NAME: xrep PROJECT_NAME: ripgrep
matrix: matrix:
# Nightly channel # Nightly channel
- TARGET: i686-pc-windows-gnu - TARGET: i686-pc-windows-gnu
@@ -39,7 +39,7 @@ before_deploy:
# TODO(burntsushi): How can we enable SSSE3 on Windows? # TODO(burntsushi): How can we enable SSSE3 on Windows?
- cargo build --release - cargo build --release
- mkdir staging - mkdir staging
- copy target\release\xrep.exe staging - copy target\release\rg.exe staging
- cd staging - cd staging
# release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc' # release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc'
- 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip * - 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip *

View File

@@ -6,23 +6,22 @@ set -ex
# Generate artifacts for release # Generate artifacts for release
mk_artifacts() { mk_artifacts() {
RUSTFLAGS="-C target-feature=+ssse3" cargo build --target $TARGET --release --features simd-accel RUSTFLAGS="-C target-feature=+ssse3" \
cargo build --target $TARGET --release --features simd-accel
} }
mk_tarball() { mk_tarball() {
# create a "staging" directory # create a "staging" directory
local td=$(mktempd) local td=$(mktempd)
local out_dir=$(pwd) local out_dir=$(pwd)
local name="${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}"
mkdir "$td/$name"
# TODO update this part to copy the artifacts that make sense for your project cp target/$TARGET/release/rg "$td/$name/"
# NOTE All Cargo build artifacts will be under the 'target/$TARGET/{debug,release}' cp {README.md,UNLICENSE,COPYING,LICENSE-MIT} "$td/$name/"
cp target/$TARGET/release/xrep $td
pushd $td pushd $td
tar czf "$out_dir/$name.tar.gz" *
# release tarball will look like 'rust-everywhere-v1.2.3-x86_64-unknown-linux-gnu.tar.gz'
tar czf $out_dir/${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.tar.gz *
popd popd
rm -r $td rm -r $td
} }

View File

@@ -42,7 +42,7 @@ run_test_suite() {
cargo test --target $TARGET cargo test --target $TARGET
# sanity check the file type # sanity check the file type
file target/$TARGET/debug/xrep file target/$TARGET/debug/rg
} }
main() { main() {

View File

@@ -6,9 +6,9 @@ authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Fast line oriented regex searching as a library. Fast line oriented regex searching as a library.
""" """
documentation = "https://github.com/BurntSushi/xrep" documentation = "https://github.com/BurntSushi/ripgrep"
homepage = "https://github.com/BurntSushi/xrep" homepage = "https://github.com/BurntSushi/ripgrep"
repository = "https://github.com/BurntSushi/xrep" repository = "https://github.com/BurntSushi/ripgrep"
readme = "README.md" readme = "README.md"
keywords = ["regex", "grep", "egrep", "search", "pattern"] keywords = ["regex", "grep", "egrep", "search", "pattern"]
license = "Unlicense/MIT" license = "Unlicense/MIT"

View File

@@ -9,11 +9,12 @@ use grep::{Grep, GrepBuilder};
use log; use log;
use num_cpus; use num_cpus;
use regex; use regex;
use term::Terminal;
use walkdir::WalkDir; use walkdir::WalkDir;
use gitignore::{Gitignore, GitignoreBuilder}; use gitignore::{Gitignore, GitignoreBuilder};
use ignore::Ignore; use ignore::Ignore;
use out::Out; use out::{Out, OutBuffer};
use printer::Printer; use printer::Printer;
use search::{InputBuffer, Searcher}; use search::{InputBuffer, Searcher};
use search_buffer::BufferSearcher; use search_buffer::BufferSearcher;
@@ -28,13 +29,13 @@ use Result;
/// If you've never heard of Docopt before, see: http://docopt.org /// If you've never heard of Docopt before, see: http://docopt.org
/// (TL;DR: The CLI parser is generated from the usage string below.) /// (TL;DR: The CLI parser is generated from the usage string below.)
const USAGE: &'static str = " const USAGE: &'static str = "
Usage: xrep [options] <pattern> [<path> ...] Usage: rg [options] <pattern> [<path> ...]
xrep [options] --files [<path> ...] rg [options] --files [<path> ...]
xrep [options] --type-list rg [options] --type-list
xrep --help rg --help
xrep --version rg --version
xrep is like the silver searcher and grep, but faster than both. rg combines the usability of the silver search with the raw speed of grep.
Common options: Common options:
-a, --text Search binary files as if they were text. -a, --text Search binary files as if they were text.
@@ -114,14 +115,14 @@ Less common options:
--mmap --mmap
Search using memory maps when possible. This is enabled by default Search using memory maps when possible. This is enabled by default
when xrep thinks it will be faster. (Note that mmap searching doesn't when ripgrep thinks it will be faster. (Note that mmap searching
current support the various context related options.) doesn't current support the various context related options.)
--no-mmap --no-mmap
Never use memory maps, even when they might be faster. Never use memory maps, even when they might be faster.
--no-ignore --no-ignore
Don't respect ignore files (.gitignore, .xrepignore, etc.) Don't respect ignore files (.gitignore, .rgignore, etc.)
--no-ignore-parent --no-ignore-parent
Don't respect ignore files in parent directories. Don't respect ignore files in parent directories.
@@ -137,7 +138,7 @@ Less common options:
(capped at 6). [default: 0] (capped at 6). [default: 0]
--version --version
Show the version number of xrep and exit. Show the version number of ripgrep and exit.
File type management options: File type management options:
--type-list --type-list
@@ -152,7 +153,7 @@ File type management options:
"; ";
/// RawArgs are the args as they are parsed from Docopt. They aren't used /// RawArgs are the args as they are parsed from Docopt. They aren't used
/// directly by the rest of xrep. /// directly by the rest of ripgrep.
#[derive(Debug, RustcDecodable)] #[derive(Debug, RustcDecodable)]
pub struct RawArgs { pub struct RawArgs {
arg_pattern: String, arg_pattern: String,
@@ -230,7 +231,7 @@ pub struct Args {
} }
impl RawArgs { impl RawArgs {
/// Convert arguments parsed into a configuration used by xrep. /// Convert arguments parsed into a configuration used by ripgrep.
fn to_args(&self) -> Result<Args> { fn to_args(&self) -> Result<Args> {
let pattern = { let pattern = {
let pattern = let pattern =
@@ -387,7 +388,7 @@ impl Args {
/// ///
/// If a CLI usage error occurred, then exit the process and print a usage /// If a CLI usage error occurred, then exit the process and print a usage
/// or error message. Similarly, if the user requested the version of /// or error message. Similarly, if the user requested the version of
/// xrep, then print the version and exit. /// ripgrep, then print the version and exit.
/// ///
/// Also, initialize a global logger. /// Also, initialize a global logger.
pub fn parse() -> Result<Args> { pub fn parse() -> Result<Args> {
@@ -409,7 +410,7 @@ impl Args {
raw.to_args().map_err(From::from) raw.to_args().map_err(From::from)
} }
/// Returns true if xrep should print the files it will search and exit /// Returns true if ripgrep should print the files it will search and exit
/// (but not do any actual searching). /// (but not do any actual searching).
pub fn files(&self) -> bool { pub fn files(&self) -> bool {
self.files self.files
@@ -438,8 +439,8 @@ impl Args {
/// Create a new printer of individual search results that writes to the /// Create a new printer of individual search results that writes to the
/// writer given. /// writer given.
pub fn printer<W: Send + io::Write>(&self, wtr: W) -> Printer<W> { pub fn printer<W: Send + Terminal>(&self, wtr: W) -> Printer<W> {
let mut p = Printer::new(wtr, self.color) let mut p = Printer::new(wtr)
.column(self.column) .column(self.column)
.context_separator(self.context_separator.clone()) .context_separator(self.context_separator.clone())
.eol(self.eol) .eol(self.eol)
@@ -454,8 +455,8 @@ impl Args {
/// Create a new printer of search results for an entire file that writes /// Create a new printer of search results for an entire file that writes
/// to the writer given. /// to the writer given.
pub fn out<W: io::Write>(&self, wtr: W) -> Out<W> { pub fn out(&self) -> Out {
let mut out = Out::new(wtr); let mut out = Out::new(self.color);
if self.heading && !self.count { if self.heading && !self.count {
out = out.file_separator(b"".to_vec()); out = out.file_separator(b"".to_vec());
} else if self.before_context > 0 || self.after_context > 0 { } else if self.before_context > 0 || self.after_context > 0 {
@@ -464,6 +465,11 @@ impl Args {
out 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. /// Return the paths that should be searched.
pub fn paths(&self) -> &[PathBuf] { pub fn paths(&self) -> &[PathBuf] {
&self.paths &self.paths
@@ -472,7 +478,7 @@ impl Args {
/// Create a new line based searcher whose configuration is taken from the /// Create a new line based searcher whose configuration is taken from the
/// command line. This searcher supports a dizzying array of features: /// command line. This searcher supports a dizzying array of features:
/// inverted matching, line counting, context control and more. /// 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, &self,
inp: &'a mut InputBuffer, inp: &'a mut InputBuffer,
printer: &'a mut Printer<W>, printer: &'a mut Printer<W>,
@@ -493,7 +499,7 @@ impl Args {
/// Create a new line based searcher whose configuration is taken from the /// Create a new line based searcher whose configuration is taken from the
/// command line. This search operates on an entire file all once (which /// command line. This search operates on an entire file all once (which
/// may have been memory mapped). /// may have been memory mapped).
pub fn searcher_buffer<'a, W: Send + io::Write>( pub fn searcher_buffer<'a, W: Send + Terminal>(
&self, &self,
printer: &'a mut Printer<W>, printer: &'a mut Printer<W>,
grep: &'a Grep, grep: &'a Grep,
@@ -518,8 +524,8 @@ impl Args {
&self.type_defs &self.type_defs
} }
/// Returns true if xrep should print the type definitions currently loaded /// Returns true if ripgrep should print the type definitions currently
/// and then exit. /// loaded and then exit.
pub fn type_list(&self) -> bool { pub fn type_list(&self) -> bool {
self.type_list self.type_list
} }

View File

@@ -9,7 +9,7 @@ The motivation for this submodule is performance and portability:
2. We could shell out to a `git` sub-command like ls-files or status, but it 2. We could shell out to a `git` sub-command like ls-files or status, but it
seems better to not rely on the existence of external programs for a search seems better to not rely on the existence of external programs for a search
tool. Besides, we need to implement this logic anyway to support things like tool. Besides, we need to implement this logic anyway to support things like
an .xrepignore file. an .rgignore file.
The key implementation detail here is that a single gitignore file is compiled The key implementation detail here is that a single gitignore file is compiled
into a single RegexSet, which can be used to report which globs match a into a single RegexSet, which can be used to report which globs match a
@@ -379,7 +379,7 @@ mod tests {
}; };
} }
const ROOT: &'static str = "/home/foobar/rust/xrep"; const ROOT: &'static str = "/home/foobar/rust/rg";
ignored!(ig1, ROOT, "months", "months"); ignored!(ig1, ROOT, "months", "months");
ignored!(ig2, ROOT, "*.lock", "Cargo.lock"); ignored!(ig2, ROOT, "*.lock", "Cargo.lock");

View File

@@ -5,7 +5,7 @@ whether a *single* file path should be searched or not.
In general, there are two ways to ignore a particular file: In general, there are two ways to ignore a particular file:
1. Specify an ignore rule in some "global" configuration, such as a 1. Specify an ignore rule in some "global" configuration, such as a
$HOME/.xrepignore or on the command line. $HOME/.rgignore or on the command line.
2. A specific ignore file (like .gitignore) found during directory traversal. 2. A specific ignore file (like .gitignore) found during directory traversal.
The `IgnoreDir` type handles ignore patterns for any one particular directory The `IgnoreDir` type handles ignore patterns for any one particular directory
@@ -24,7 +24,7 @@ use types::Types;
const IGNORE_NAMES: &'static [&'static str] = &[ const IGNORE_NAMES: &'static [&'static str] = &[
".gitignore", ".gitignore",
".agignore", ".agignore",
".xrepignore", ".rgignore",
]; ];
/// Represents an error that can occur when parsing a gitignore file. /// Represents an error that can occur when parsing a gitignore file.
@@ -257,8 +257,8 @@ pub struct IgnoreDir {
/// A single accumulation of glob patterns for this directory, matched /// A single accumulation of glob patterns for this directory, matched
/// using gitignore semantics. /// using gitignore semantics.
/// ///
/// This will include patterns from xrepignore as well. The patterns are /// This will include patterns from rgignore as well. The patterns are
/// ordered so that precedence applies automatically (e.g., xrepignore /// ordered so that precedence applies automatically (e.g., rgignore
/// patterns procede gitignore patterns). /// patterns procede gitignore patterns).
gi: Option<Gitignore>, gi: Option<Gitignore>,
// TODO(burntsushi): Matching other types of glob patterns that don't // TODO(burntsushi): Matching other types of glob patterns that don't
@@ -422,7 +422,7 @@ mod tests {
}; };
} }
const ROOT: &'static str = "/home/foobar/rust/xrep"; const ROOT: &'static str = "/home/foobar/rust/rg";
ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs"); ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs");
ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs"); ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs");

View File

@@ -39,7 +39,7 @@ use term::Terminal;
use walkdir::DirEntry; use walkdir::DirEntry;
use args::Args; use args::Args;
use out::Out; use out::{NoColorTerminal, Out, OutBuffer};
use printer::Printer; use printer::Printer;
use search::InputBuffer; use search::InputBuffer;
@@ -90,7 +90,8 @@ fn run(args: Args) -> Result<u64> {
return run_types(args); return run_types(args);
} }
let args = Arc::new(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 workers = vec![];
let mut workq = { let mut workq = {
@@ -101,7 +102,7 @@ fn run(args: Args) -> Result<u64> {
out: out.clone(), out: out.clone(),
chan_work: stealer.clone(), chan_work: stealer.clone(),
inpbuf: args.input_buffer(), inpbuf: args.input_buffer(),
outbuf: Some(vec![]), outbuf: Some(outbuf.clone()),
grep: args.grep(), grep: args.grep(),
match_count: 0, match_count: 0,
}; };
@@ -129,7 +130,8 @@ fn run(args: Args) -> Result<u64> {
} }
fn run_files(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; let mut file_count = 0;
for p in args.paths() { for p in args.paths() {
if p == Path::new("-") { if p == Path::new("-") {
@@ -146,7 +148,8 @@ fn run_files(args: Args) -> Result<u64> {
} }
fn run_types(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; let mut ty_count = 0;
for def in args.type_defs() { for def in args.type_defs() {
printer.type_def(def); printer.type_def(def);
@@ -168,10 +171,10 @@ enum WorkReady {
struct Worker { struct Worker {
args: Arc<Args>, args: Arc<Args>,
out: Arc<Mutex<Out<io::Stdout>>>, out: Arc<Mutex<Out>>,
chan_work: Stealer<Work>, chan_work: Stealer<Work>,
inpbuf: InputBuffer, inpbuf: InputBuffer,
outbuf: Option<Vec<u8>>, outbuf: Option<OutBuffer>,
grep: Grep, grep: Grep,
match_count: u64, match_count: u64,
} }
@@ -203,12 +206,12 @@ impl Worker {
let mut out = self.out.lock().unwrap(); let mut out = self.out.lock().unwrap();
out.write(&outbuf); out.write(&outbuf);
} }
self.outbuf = Some(outbuf.into_inner()); self.outbuf = Some(outbuf);
} }
self.match_count self.match_count
} }
fn do_work<W: Send + io::Write>( fn do_work<W: Send + Terminal>(
&mut self, &mut self,
printer: &mut Printer<W>, printer: &mut Printer<W>,
work: WorkReady, 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, &mut self,
printer: &mut Printer<W>, printer: &mut Printer<W>,
path: &Path, path: &Path,
@@ -256,7 +259,7 @@ impl Worker {
).run().map_err(From::from) ).run().map_err(From::from)
} }
fn search_mmap<W: Send + io::Write>( fn search_mmap<W: Send + Terminal>(
&mut self, &mut self,
printer: &mut Printer<W>, printer: &mut Printer<W>,
path: &Path, path: &Path,

View File

@@ -1,10 +1,40 @@
use std::io::{self, Write}; 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)] #[cfg(windows)]
use term::WinConsole; use term::WinConsole;
use printer::Writer; use terminal::TerminfoTerminal;
pub type StdoutTerminal = Box<Terminal<Output=io::Stdout> + Send>;
/// Gets a terminal that supports color if available.
#[cfg(windows)]
fn term_stdout(color: bool) -> StdoutTerminal {
let stdout = io::stdout();
WinConsole::new(stdout)
.ok()
.map(|t| Box::new(t) as StdoutTerminal)
.unwrap_or_else(|| {
let stdout = io::stdout();
Box::new(NoColorTerminal::new(stdout)) as StdoutTerminal
})
}
/// Gets a terminal that supports color if available.
#[cfg(not(windows))]
fn term_stdout(color: bool) -> StdoutTerminal {
let stdout = 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 /// Out controls the actual output of all search results for a particular file
/// to the end user. /// to the end user.
@@ -12,34 +42,17 @@ use printer::Writer;
/// (The difference between Out and Printer is that a Printer works with /// (The difference between Out and Printer is that a Printer works with
/// individual search results where as Out works with search results for each /// 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.) /// file as a whole. For example, it knows when to print a file separator.)
pub struct Out<W: io::Write> { pub struct Out {
wtr: io::BufWriter<W>, term: StdoutTerminal,
term: Option<Box<StdoutTerminal>>,
printed: bool, printed: bool,
file_separator: Option<Vec<u8>>, file_separator: Option<Vec<u8>>,
} }
/// This is like term::stdout, but on Windows always uses WinConsole instead impl Out {
/// 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> {
/// Create a new Out that writes to the wtr given. /// Create a new Out that writes to the wtr given.
pub fn new(wtr: W) -> Out<W> { pub fn new(color: bool) -> Out {
Out { Out {
wtr: io::BufWriter::new(wtr), term: term_stdout(color),
term: term_stdout(),
printed: false, printed: false,
file_separator: None, file_separator: None,
} }
@@ -49,39 +62,422 @@ impl<W: io::Write> Out<W> {
/// By default, no separator is printed. /// By default, no separator is printed.
/// ///
/// If sep is empty, then no file 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.file_separator = Some(sep);
self self
} }
/// Write the search results of a single file to the underlying wtr and /// Write the search results of a single file to the underlying wtr and
/// flush wtr. /// 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 let Some(ref sep) = self.file_separator {
if self.printed { if self.printed {
let _ = self.wtr.write_all(sep); let _ = self.term.write_all(sep);
let _ = self.wtr.write_all(b"\n"); let _ = self.term.write_all(b"\n");
} }
} }
match *buf { match *buf {
Writer::Colored(ref tt) => { OutBuffer::Colored(ref tt) => {
let _ = self.wtr.write_all(tt.get_ref()); let _ = self.term.write_all(tt.get_ref());
} }
Writer::Windows(ref w) => { OutBuffer::Windows(ref w) => {
match self.term { w.print_stdout(&mut self.term);
None => {
let _ = self.wtr.write_all(w.get_ref());
}
Some(ref mut stdout) => {
w.print_stdout(stdout);
}
}
} }
Writer::NoColor(ref buf) => { OutBuffer::NoColor(ref buf) => {
let _ = self.wtr.write_all(buf); let _ = self.term.write_all(buf);
} }
} }
let _ = self.wtr.flush(); let _ = self.term.flush();
self.printed = true; 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
}
}

View File

@@ -1,17 +1,11 @@
use std::io::{self, Write};
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use regex::bytes::Regex; use regex::bytes::Regex;
use term::{self, StdoutTerminal, Terminal}; use term::{Attr, Terminal};
use term::color::*; use term::color;
use term::terminfo::TermInfo;
use terminal::TerminfoTerminal;
use types::FileTypeDef; use types::FileTypeDef;
use self::Writer::*;
/// Printer encapsulates all output logic for searching. /// Printer encapsulates all output logic for searching.
/// ///
/// Note that we currently ignore all write errors. It's probably worthwhile /// 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. /// writes to memory, neither of which commonly fail.
pub struct Printer<W> { pub struct Printer<W> {
/// The underlying writer. /// The underlying writer.
wtr: Writer<W>, wtr: W,
/// Whether anything has been printed to wtr yet. /// Whether anything has been printed to wtr yet.
has_printed: bool, has_printed: bool,
/// Whether to show column numbers for the first match or not. /// Whether to show column numbers for the first match or not.
@@ -42,13 +36,11 @@ pub struct Printer<W> {
with_filename: bool, with_filename: bool,
} }
impl<W: Send + io::Write> Printer<W> { impl<W: Send + Terminal> Printer<W> {
/// Create a new printer that writes to wtr. /// Create a new printer that writes to wtr.
/// pub fn new(wtr: W) -> Printer<W> {
/// `color` should be true if the printer should try to use coloring.
pub fn new(wtr: W, color: bool) -> Printer<W> {
Printer { Printer {
wtr: Writer::new(wtr, color), wtr: wtr,
has_printed: false, has_printed: false,
column: false, column: false,
context_separator: "--".to_string().into_bytes(), context_separator: "--".to_string().into_bytes(),
@@ -115,7 +107,7 @@ impl<W: Send + io::Write> Printer<W> {
} }
/// Flushes the underlying writer and returns it. /// 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(); let _ = self.wtr.flush();
self.wtr self.wtr
} }
@@ -201,15 +193,15 @@ impl<W: Send + io::Write> Printer<W> {
} }
pub fn write_match(&mut self, re: &Regex, buf: &[u8]) { pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
if !self.wtr.is_color() { if !self.wtr.supports_color() {
self.write(buf); self.write(buf);
return; return;
} }
let mut last_written = 0; let mut last_written = 0;
for (s, e) in re.find_iter(buf) { for (s, e) in re.find_iter(buf) {
self.write(&buf[last_written..s]); self.write(&buf[last_written..s]);
let _ = self.wtr.fg(BRIGHT_RED); let _ = self.wtr.fg(color::BRIGHT_RED);
let _ = self.wtr.attr(term::Attr::Bold); let _ = self.wtr.attr(Attr::Bold);
self.write(&buf[s..e]); self.write(&buf[s..e]);
let _ = self.wtr.reset(); let _ = self.wtr.reset();
last_written = e; last_written = e;
@@ -241,24 +233,24 @@ impl<W: Send + io::Write> Printer<W> {
} }
fn write_heading<P: AsRef<Path>>(&mut self, path: P) { fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
if self.wtr.is_color() { if self.wtr.supports_color() {
let _ = self.wtr.fg(BRIGHT_GREEN); let _ = self.wtr.fg(color::BRIGHT_GREEN);
let _ = self.wtr.attr(term::Attr::Bold); let _ = self.wtr.attr(Attr::Bold);
} }
self.write(path.as_ref().to_string_lossy().as_bytes()); self.write(path.as_ref().to_string_lossy().as_bytes());
self.write_eol(); self.write_eol();
if self.wtr.is_color() { if self.wtr.supports_color() {
let _ = self.wtr.reset(); let _ = self.wtr.reset();
} }
} }
fn line_number(&mut self, n: u64, sep: u8) { fn line_number(&mut self, n: u64, sep: u8) {
if self.wtr.is_color() { if self.wtr.supports_color() {
let _ = self.wtr.fg(BRIGHT_BLUE); let _ = self.wtr.fg(color::BRIGHT_BLUE);
let _ = self.wtr.attr(term::Attr::Bold); let _ = self.wtr.attr(Attr::Bold);
} }
self.write(n.to_string().as_bytes()); self.write(n.to_string().as_bytes());
if self.wtr.is_color() { if self.wtr.supports_color() {
let _ = self.wtr.reset(); let _ = self.wtr.reset();
} }
self.write(&[sep]); self.write(&[sep]);
@@ -277,289 +269,3 @@ impl<W: Send + io::Write> Printer<W> {
self.write(&[eol]); 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
}
}

View File

@@ -11,6 +11,7 @@ use std::path::{Path, PathBuf};
use grep::{Grep, Match}; use grep::{Grep, Match};
use memchr::{memchr, memrchr}; use memchr::{memchr, memrchr};
use term::Terminal;
use printer::Printer; 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. /// Create a new searcher.
/// ///
/// `inp` is a reusable input buffer that is used as scratch space by this /// `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 grep::{Grep, GrepBuilder};
use term::Terminal; use term::Terminal;
use out::OutBuffer;
use printer::Printer; use printer::Printer;
use super::{InputBuffer, Searcher, start_of_previous_lines}; use super::{InputBuffer, Searcher, start_of_previous_lines};
@@ -731,7 +733,7 @@ fn main() {
&Path::new("/baz.rs") &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>( fn search_smallcap<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str, pat: &str,
@@ -739,7 +741,8 @@ fn main() {
mut map: F, mut map: F,
) -> (u64, String) { ) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(1); 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 grep = GrepBuilder::new(pat).build().unwrap();
let count = { let count = {
let searcher = Searcher::new( let searcher = Searcher::new(
@@ -755,7 +758,8 @@ fn main() {
mut map: F, mut map: F,
) -> (u64, String) { ) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(4096); 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 grep = GrepBuilder::new(pat).build().unwrap();
let count = { let count = {
let searcher = Searcher::new( let searcher = Searcher::new(

View File

@@ -1,8 +1,8 @@
use std::cmp; use std::cmp;
use std::io;
use std::path::Path; use std::path::Path;
use grep::Grep; use grep::Grep;
use term::Terminal;
use printer::Printer; use printer::Printer;
use search::{IterLines, Options, count_lines, is_binary}; use search::{IterLines, Options, count_lines, is_binary};
@@ -18,7 +18,7 @@ pub struct BufferSearcher<'a, W: 'a> {
last_line: usize, last_line: usize,
} }
impl<'a, W: Send + io::Write> BufferSearcher<'a, W> { impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
pub fn new( pub fn new(
printer: &'a mut Printer<W>, printer: &'a mut Printer<W>,
grep: &'a Grep, grep: &'a Grep,
@@ -146,6 +146,7 @@ mod tests {
use grep::{Grep, GrepBuilder}; use grep::{Grep, GrepBuilder};
use term::Terminal; use term::Terminal;
use out::OutBuffer;
use printer::Printer; use printer::Printer;
use super::BufferSearcher; use super::BufferSearcher;
@@ -184,14 +185,15 @@ fn main() {
&Path::new("/baz.rs") &Path::new("/baz.rs")
} }
type TestSearcher<'a> = BufferSearcher<'a, Vec<u8>>; type TestSearcher<'a> = BufferSearcher<'a, OutBuffer>;
fn search<F: FnMut(TestSearcher) -> TestSearcher>( fn search<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str, pat: &str,
haystack: &str, haystack: &str,
mut map: F, mut map: F,
) -> (u64, String) { ) -> (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 grep = GrepBuilder::new(pat).build().unwrap();
let count = { let count = {
let searcher = BufferSearcher::new( let searcher = BufferSearcher::new(

View File

@@ -1,6 +1,6 @@
/*! /*!
This io module contains various platform specific functions for detecting This io module contains various platform specific functions for detecting
how xrep is being used. e.g., Is stdin being piped into it? Is stdout being how ripgrep is being used. e.g., Is stdin being piped into it? Is stdout being
redirected to a file? etc... We use this information to tweak various default redirected to a file? etc... We use this information to tweak various default
configuration parameters such as colors and match formatting. configuration parameters such as colors and match formatting.
*/ */