summaryrefslogtreecommitdiffstats
path: root/src/args2.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/args2.rs')
-rw-r--r--src/args2.rs1384
1 files changed, 0 insertions, 1384 deletions
diff --git a/src/args2.rs b/src/args2.rs
deleted file mode 100644
index 6a5d1502..00000000
--- a/src/args2.rs
+++ /dev/null
@@ -1,1384 +0,0 @@
-use std::cmp;
-use std::env;
-use std::ffi::OsStr;
-use std::fs::File;
-use std::io::{self, BufRead};
-use std::path::{Path, PathBuf};
-use std::sync::Arc;
-
-use atty;
-use clap;
-use grep2::matcher::LineTerminator;
-use grep2::searcher::{
- BinaryDetection, Encoding, MmapChoice, Searcher, SearcherBuilder,
-};
-use grep2::printer::{
- ColorSpecs, Stats,
- JSON, JSONBuilder,
- Standard, StandardBuilder,
- Summary, SummaryBuilder, SummaryKind,
-};
-use grep2::regex::{RegexMatcher, RegexMatcherBuilder};
-use ignore::overrides::{Override, OverrideBuilder};
-use ignore::types::{FileTypeDef, Types, TypesBuilder};
-use ignore::{Walk, WalkBuilder, WalkParallel};
-use log;
-use num_cpus;
-use path_printer::{PathPrinter, PathPrinterBuilder};
-use regex::{self, Regex};
-use same_file::Handle;
-use termcolor::{
- WriteColor,
- BufferedStandardStream, BufferWriter, ColorChoice, StandardStream,
-};
-
-use app;
-use config;
-use logger::Logger;
-use messages::{set_messages, set_ignore_messages};
-use search::{PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder};
-use subject::SubjectBuilder;
-use unescape::{escape, unescape};
-use Result;
-
-/// The command that ripgrep should execute based on the command line
-/// configuration.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Command {
- /// Search using exactly one thread.
- Search,
- /// Search using possibly many threads.
- SearchParallel,
- /// The command line parameters suggest that a search should occur, but
- /// ripgrep knows that a match can never be found (e.g., no given patterns
- /// or --max-count=0).
- SearchNever,
- /// Show the files that would be searched, but don't actually search them,
- /// and use exactly one thread.
- Files,
- /// Show the files that would be searched, but don't actually search them,
- /// and perform directory traversal using possibly many threads.
- FilesParallel,
- /// List all file type definitions configured, including the default file
- /// types and any additional file types added to the command line.
- Types,
-}
-
-impl Command {
- /// Returns true if and only if this command requires executing a search.
- fn is_search(&self) -> bool {
- use self::Command::*;
-
- match *self {
- Search | SearchParallel => true,
- SearchNever | Files | FilesParallel | Types => false,
- }
- }
-}
-
-/// The primary configuration object used throughout ripgrep. It provides a
-/// high-level convenient interface to the provided command line arguments.
-///
-/// An `Args` object is cheap to clone and can be used from multiple threads
-/// simultaneously.
-#[derive(Clone, Debug)]
-pub struct Args(Arc<ArgsImp>);
-
-#[derive(Clone, Debug)]
-struct ArgsImp {
- /// Mid-to-low level routines for extracting CLI arguments.
- matches: ArgMatches,
- /// The patterns provided at the command line and/or via the -f/--file
- /// flag. This may be empty.
- patterns: Vec<String>,
- /// A matcher built from the patterns.
- ///
- /// It's important that this is only built once, since building this goes
- /// through regex compilation and various types of analyses. That is, if
- /// you need many of theses (one per thread, for example), it is better to
- /// build it once and then clone it.
- matcher: PatternMatcher,
- /// The paths provided at the command line. This is guaranteed to be
- /// non-empty. (If no paths are provided, then a default path is created.)
- paths: Vec<PathBuf>,
- /// Returns true if and only if `paths` had to be populated with a single
- /// default path.
- using_default_path: bool,
-}
-
-impl Args {
- /// Parse the command line arguments for this process.
- ///
- /// 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
- /// ripgrep, then print the version and exit.
- ///
- /// Also, initialize a global logger.
- pub fn parse() -> Result<Args> {
- // We parse the args given on CLI. This does not include args from
- // the config. We use the CLI args as an initial configuration while
- // trying to parse config files. If a config file exists and has
- // arguments, then we re-parse argv, otherwise we just use the matches
- // we have here.
- let early_matches = ArgMatches::new(app::app().get_matches());
- set_messages(!early_matches.is_present("no-messages"));
- set_ignore_messages(!early_matches.is_present("no-ignore-messages"));
-
- if let Err(err) = Logger::init() {
- errored!("failed to initialize logger: {}", err);
- }
- if early_matches.is_present("trace") {
- log::set_max_level(log::LevelFilter::Trace);
- } else if early_matches.is_present("debug") {
- log::set_max_level(log::LevelFilter::Debug);
- } else {
- log::set_max_level(log::LevelFilter::Warn);
- }
-
- let matches = early_matches.reconfigure();
- // The logging level may have changed if we brought in additional
- // arguments from a configuration file, so recheck it and set the log
- // level as appropriate.
- if matches.is_present("trace") {
- log::set_max_level(log::LevelFilter::Trace);
- } else if matches.is_present("debug") {
- log::set_max_level(log::LevelFilter::Debug);
- } else {
- log::set_max_level(log::LevelFilter::Warn);
- }
- set_messages(!matches.is_present("no-messages"));
- set_ignore_messages(!matches.is_present("no-ignore-messages"));
- matches.to_args()
- }
-
- /// Return direct access to command line arguments.
- fn matches(&self) -> &ArgMatches {
- &self.0.matches
- }
-
- /// Return the patterns found in the command line arguments. This includes
- /// patterns read via the -f/--file flags.
- fn patterns(&self) -> &[String] {
- &self.0.patterns
- }
-
- /// Return the matcher builder from the patterns.
- fn matcher(&self) -> &PatternMatcher {
- &self.0.matcher
- }
-
- /// Return the paths found in the command line arguments. This is
- /// guaranteed to be non-empty. In the case where no explicit arguments are
- /// provided, a single default path is provided automatically.
- fn paths(&self) -> &[PathBuf] {
- &self.0.paths
- }
-
- /// Returns true if and only if `paths` had to be populated with a default
- /// path, which occurs only when no paths were given as command line
- /// arguments.
- fn using_default_path(&self) -> bool {
- self.0.using_default_path
- }
-
- /// Return the printer that should be used for formatting the output of
- /// search results.
- ///
- /// The returned printer will write results to the given writer.
- fn printer<W: WriteColor>(&self, wtr: W) -> Result<Printer<W>> {
- match self.matches().output_kind() {
- OutputKind::Standard => {
- let separator_search = self.command()? == Command::Search;
- self.matches()
- .printer_standard(self.paths(), wtr, separator_search)
- .map(Printer::Standard)
- }
- OutputKind::Summary => {
- self.matches()
- .printer_summary(self.paths(), wtr)
- .map(Printer::Summary)
- }
- OutputKind::JSON => {
- self.matches()
- .printer_json(wtr)
- .map(Printer::JSON)
- }
- }
- }
-}
-
-/// High level public routines for building data structures used by ripgrep
-/// from command line arguments.
-impl Args {
- /// Create a new buffer writer for multi-threaded printing with color
- /// support.
- pub fn buffer_writer(&self) -> Result<BufferWriter> {
- let mut wtr = BufferWriter::stdout(self.matches().color_choice());
- wtr.separator(self.matches().file_separator()?);
- Ok(wtr)
- }
-
- /// Return the high-level command that ripgrep should run.
- pub fn command(&self) -> Result<Command> {
- let is_one_search = self.matches().is_one_search(self.paths());
- let threads = self.matches().threads()?;
- let one_thread = is_one_search || threads == 1;
-
- Ok(if self.matches().is_present("type-list") {
- Command::Types
- } else if self.matches().is_present("files") {
- if one_thread {
- Command::Files
- } else {
- Command::FilesParallel
- }
- } else if self.matches().can_never_match(self.patterns()) {
- Command::SearchNever
- } else if one_thread {
- Command::Search
- } else {
- Command::SearchParallel
- })
- }
-
- /// Builder a path printer that can be used for printing just file paths,
- /// with optional color support.
- ///
- /// The printer will print paths to the given writer.
- pub fn path_printer<W: WriteColor>(
- &self,
- wtr: W,
- ) -> Result<PathPrinter<W>> {
- let mut builder = PathPrinterBuilder::new();
- builder
- .color_specs(self.matches().color_specs()?)
- .separator(self.matches().path_separator()?)
- .terminator(self.matches().path_terminator().unwrap_or(b'\n'));
- Ok(builder.build(wtr))
- }
-
- /// Returns true if and only if the search should quit after finding the
- /// first match.
- pub fn quit_after_match(&self) -> Result<bool> {
- Ok(self.matches().is_present("quiet") && self.stats()?.is_none())
- }
-
- /// Build a worker for executing searches.
- ///
- /// Search results are written to the given writer.
- pub fn search_worker<W: WriteColor>(
- &self,
- wtr: W,
- ) -> Result<SearchWorker<W>> {
- let matcher = self.matcher().clone();
- let printer = self.printer(wtr)?;
- let searcher = self.matches().searcher(self.paths())?;
- let mut builder = SearchWorkerBuilder::new();
- builder
- .preprocessor(self.matches().preprocessor())
- .search_zip(self.matches().is_present("search-zip"));
- Ok(builder.build(matcher, searcher, printer))
- }
-
- /// Returns a zero value for tracking statistics if and only if it has been
- /// requested.
- ///
- /// When this returns a `Stats` value, then it is guaranteed that the
- /// search worker will be configured to track statistics as well.
- pub fn stats(&self) -> Result<Option<Stats>> {
- Ok(if self.command()?.is_search() && self.matches().stats() {
- Some(Stats::new())
- } else {
- None
- })
- }
-
- /// Return a builder for constructing subjects. A subject represents a
- /// single unit of something to search. Typically, this corresponds to a
- /// file or a stream such as stdin.
- pub fn subject_builder(&self) -> SubjectBuilder {
- let mut builder = SubjectBuilder::new();
- builder
- .strip_dot_prefix(self.using_default_path())
- .skip(self.matches().stdout_handle());
- builder
- }
-
- /// Execute the given function with a writer to stdout that enables color
- /// support based on the command line configuration.
- pub fn stdout(&self) -> Box<WriteColor + Send> {
- let color_choice = self.matches().color_choice();
- if atty::is(atty::Stream::Stdout) {
- Box::new(StandardStream::stdout(color_choice))
- } else {
- Box::new(BufferedStandardStream::stdout(color_choice))
- }
- }
-
- /// Return the type definitions compiled into ripgrep.
- ///
- /// If there was a problem reading and parsing the type definitions, then
- /// this returns an error.
- pub fn type_defs(&self) -> Result<Vec<FileTypeDef>> {
- Ok(self.matches().types()?.definitions().to_vec())
- }
-
- /// Return a walker that never uses additional threads.
- pub fn walker(&self) -> Result<Walk> {
- Ok(self.matches().walker_builder(self.paths())?.build())
- }
-
- /// Return a walker that never uses additional threads.
- pub fn walker_parallel(&self) -> Result<WalkParallel> {
- Ok(self.matches().walker_builder(self.paths())?.build_parallel())
- }
-}
-
-/// `ArgMatches` wraps `clap::ArgMatches` and provides semantic meaning to
-/// the parsed arguments.
-#[derive(Clone, Debug)]
-struct ArgMatches(clap::ArgMatches<'static>);
-
-/// The output format. Generally, this corresponds to the printer that ripgrep
-/// uses to show search results.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum OutputKind {
- /// Classic grep-like or ack-like format.
- Standard,
- /// Show matching files and possibly the number of matches in each file.
- Summary,
- /// Emit match information in the JSON Lines format.
- JSON,
-}
-
-impl ArgMatches {
- /// Create an ArgMatches from clap's parse result.
- fn new(clap_matches: clap::ArgMatches<'static>) -> ArgMatches {
- ArgMatches(clap_matches)
- }
-
- /// Run clap and return the matches using a config file if present. If clap
- /// determines a problem with the user provided arguments (or if --help or
- /// --version are given), then an error/usage/version will be printed and
- /// the process will exit.
- ///
- /// If there are no additional arguments from the environment (e.g., a
- /// config file), then the given matches are returned as is.
- fn reconfigure(self) -> ArgMatches {
- // If the end user says no config, then respect it.
- if self.is_present("no-config") {
- debug!("not reading config files because --no-config is present");
- return self;
- }
- // If the user wants ripgrep to use a config file, then parse args
- // from that first.
- let mut args = config::args();
- if args.is_empty() {
- return self;
- }
- let mut cliargs = env::args_os();
- if let Some(bin) = cliargs.next() {
- args.insert(0, bin);
- }
- args.extend(cliargs);
- debug!("final argv: {:?}", args);
- ArgMatches::new(app::app().get_matches_from(args))
- }
-
- /// Convert the result of parsing CLI arguments into ripgrep's higher level
- /// configuration structure.
- fn to_args(self) -> Result<Args> {
- // We compute these once since they could be large.
- let patterns = self.patterns()?;
- let matcher = self.matcher(&patterns)?;
- let mut paths = self.paths();
- let using_default_path =
- if paths.is_empty() {
- paths.push(self.path_default());
- true
- } else {
- false
- };
- Ok(Args(Arc::new(ArgsImp {
- matches: self,
- patterns: patterns,
- matcher: matcher,
- paths: paths,
- using_default_path: using_default_path,
- })))
- }
-}
-
-/// High level routines for converting command line arguments into various
-/// data structures used by ripgrep.
-///
-/// Methods are sorted alphabetically.
-impl ArgMatches {
- /// Return the matcher that should be used for searching.
- ///
- /// If there was a problem building the matcher (e.g., a syntax error),
- /// then this returns an error.
- fn matcher(&self, patterns: &[String]) -> Result<PatternMatcher> {
- let matcher = self.matcher_rust(patterns)?;
- Ok(PatternMatcher::RustRegex(matcher))
- }
-
- /// Build a matcher using Rust's regex engine.
- ///
- /// If there was a problem building the matcher (such as a regex syntax
- /// error), then an error is returned.
- fn matcher_rust(&self, patterns: &[String]) -> Result<RegexMatcher> {
- let mut builder = RegexMatcherBuilder::new();
- builder
- .case_smart(self.case_smart())
- .case_insensitive(self.case_insensitive())
- .multi_line(true)
- .dot_matches_new_line(false)
- .unicode(true)
- .octal(false)
- .word(self.is_present("word-regexp"));
- if !self.is_present("multiline") {
- builder.line_terminator(Some(b'\n'));
- }
- if let Some(limit) = self.regex_size_limit()? {
- builder.size_limit(limit);
- }
- if let Some(limit) = self.dfa_size_limit()? {
- builder.dfa_size_limit(limit);
- }
- Ok(builder.build(&patterns.join("|"))?)
- }
-
- /// Build a JSON printer that writes results to the given writer.
- fn printer_json<W: io::Write>(&self, wtr: W) -> Result<JSON<W>> {
- let mut builder = JSONBuilder::new();
- builder
- .pretty(false)
- .max_matches(self.max_count()?)
- .always_begin_end(false);
- Ok(builder.build(wtr))
- }
-
- /// Build a Standard printer that writes results to the given writer.
- ///
- /// The given paths are used to configure aspects of the printer.
- ///
- /// If `separator_search` is true, then the returned printer will assume
- /// the responsibility of printing a separator between each set of
- /// search results, when appropriate (e.g., when contexts are enabled).
- /// When it's set to false, the caller is responsible for handling
- /// separators.
- ///
- /// In practice, we want the printer to handle it in the single threaded
- /// case but not in the multi-threaded case.
- fn printer_standard<W: WriteColor>(
- &self,
- paths: &[PathBuf],
- wtr: W,
- separator_search: bool,
- ) -> Result<Standard<W>> {
- let mut builder = StandardBuilder::new();
- builder
- .color_specs(self.color_specs()?)
- .stats(self.stats())
- .heading(self.heading())
- .path(self.with_filename(paths))
- .only_matching(self.is_present("only-matching"))
- .per_match(self.is_present("vimgrep"))
- .replacement(self.replacement())
- .max_columns(self.max_columns()?)
- .max_matches(self.max_count()?)
- .column(self.column())
- .byte_offset(self.is_present("byte-offset"))
- .trim_ascii(false)
- .separator_search(None)
- .separator_context(Some(self.context_separator()))
- .separator_field_match(b":".to_vec())
- .separator_field_context(b"-".to_vec())
- .separator_path(self.path_separator()?)
- .path_terminator(self.path_terminator());
- if separator_search {
- builder.separator_search(self.file_separator()?);
- }
- Ok(builder.build(wtr))
- }
-
- /// Build a Summary printer that writes results to the given writer.
- ///
- /// The given paths are used to configure aspects of the printer.
- ///
- /// This panics if the output format is not `OutputKind::Summary`.
- fn printer_summary<W: WriteColor>(
- &self,
- paths: &[PathBuf],
- wtr: W,
- ) -> Result<Summary<W>> {
- let mut builder = SummaryBuilder::new();
- builder
- .kind(self.summary_kind().expect("summary format"))
- .color_specs(self.color_specs()?)
- .stats(self.stats())
- .path(self.with_filename(paths))
- .max_matches(self.max_count()?)
- .separator_field(b":".to_vec())
- .separator_path(self.path_separator()?)
- .path_terminator(self.path_terminator());
- Ok(builder.build(wtr))
- }
-
- /// Build a searcher from the command line parameters.
- fn searcher(&self, paths: &[PathBuf]) -> Result<Searcher> {
- let (ctx_before, ctx_after) = self.contexts()?;
- let mut builder = SearcherBuilder::new();
- builder
- .line_terminator(LineTerminator::byte(b'\n'))
- .invert_match(self.is_present("invert-match"))
- .line_number(self.line_number(paths))
- .multi_line(self.is_present("multiline"))
- .before_context(ctx_before)
- .after_context(ctx_after)
- .passthru(self.is_present("passthru"))
- .memory_map(self.mmap_choice(paths))
- .binary_detection(self.binary_detection())
- .encoding(self.encoding()?);
- Ok(builder.build())
- }
-
- /// Return a builder for recursively traversing a directory while
- /// respecting ignore rules.
- ///
- /// If there was a problem parsing the CLI arguments necessary for
- /// constructing the builder, then this returns an error.
- fn walker_builder(&self, paths: &[PathBuf]) -> Result<WalkBuilder> {
- let mut builder = WalkBuilder::new(&paths[0]);
- for path in &paths[1..] {
- builder.add(path);
- }
- for path in self.ignore_paths() {
- if let Some(err) = builder.add_ignore(path) {
- ignore_message!("{}", err);
- }
- }
- builder
- .max_depth(self.usize_of("max-depth")?)
- .follow_links(self.is_present("follow"))
- .max_filesize(self.max_file_size()?)
- .threads(self.threads()?)
- .overrides(self.overrides()?)
- .types(self.types()?)
- .hidden(!self.hidden())
- .parents(!self.no_ignore_parent())
- .ignore(!self.no_ignore())
- .git_global(
- !self.no_ignore()
- && !self.no_ignore_vcs()
- && !self.no_ignore_global())
- .git_ignore(!self.no_ignore() && !self.no_ignore_vcs())
- .git_exclude(!self.no_ignore() && !self.no_ignore_vcs());
- if !self.no_ignore() {
- builder.add_custom_ignore_filename(".rgignore");
- }
- if self.is_present("sort-files") {
- builder.sort_by_file_name(|a, b| a.cmp(b));
- }
- Ok(builder)
- }
-}
-
-/// Mid level routines for converting command line arguments into various types
-/// of data structures.
-///
-/// Methods are sorted alphabetically.
-impl ArgMatches {
- /// Returns the form of binary detection to perform.
- fn binary_detection(&self) -> BinaryDetection {
- if self.is_present("text") || self.unrestricted_count() >= 3 {
- BinaryDetection::none()
- } else {
- BinaryDetection::quit(b'\x00')
- }
- }
-
- /// Returns true if the command line configuration implies that a match
- /// can never be shown.
- fn can_never_match(&self, patterns: &[String]) -> bool {
- patterns.is_empty() || self.max_count().ok() == Some(Some(0))
- }
-
- /// Returns true if and only if case should be ignore.
- ///
- /// If --case-sensitive is present, then case is never ignored, even if
- /// --ignore-case is present.
- fn case_insensitive(&self) -> bool {
- self.is_present("ignore-case") && !self.is_present("case-sensitive")
- }
-
- /// Returns true if and only if smart case has been enabled.
- ///
- /// If either --ignore-case of --case-sensitive are present, then smart
- /// case is disabled.
- fn case_smart(&self) -> bool {
- self.is_present("smart-case")
- && !self.is_present("ignore-case")
- && !self.is_present("case-sensitive")
- }
-
- /// Returns the user's color choice based on command line parameters and
- /// environment.
- fn color_choice(&self) -> ColorChoice {
- let preference = match self.value_of_lossy("color") {
- None => "auto".to_string(),
- Some(v) => v,
- };
- if preference == "always" {
- ColorChoice::Always
- } else if preference == "ansi" {
- ColorChoice::AlwaysAnsi
- } else if preference == "auto" {
- if atty::is(atty::Stream::Stdout) || self.is_present("pretty") {
- ColorChoice::Auto
- } else {
- ColorChoice::Never
- }
- } else {
- ColorChoice::Never
- }
- }
-
- /// Returns the color specifications given by the user on the CLI.
- ///
- /// If the was a problem parsing any of the provided specs, then an error
- /// is returned.
- fn color_specs(&self) -> Result<ColorSpecs> {
- // Start with a default set of color specs.
- let mut specs = vec![
- #[cfg(unix)]
- "path:fg:magenta".parse().unwrap(),
- #[cfg(windows)]
- "path:fg:cyan".parse().unwrap(),
- "line:fg:green".parse().unwrap(),
- "match:fg:red".parse().unwrap(),
- "match:style:bold".parse().unwrap(),
- ];
- for spec_str in self.values_of_lossy_vec("colors") {
- specs.push(spec_str.parse()?);
- }
- Ok(ColorSpecs::new(&specs))
- }
-
- /// Returns true if and only if column numbers should be shown.
- fn column(&self) -> bool {
- if self.is_present("no-column") {
- return false;
- }
- self.is_present("column") || self.is_present("vimgrep")
- }
-
- /// Returns the before and after contexts from the command line.
- ///
- /// If a context setting was absent, then `0` is returned.
- ///
- /// If there was a problem parsing the values from the user as an integer,
- /// then an error is returned.
- fn contexts(&self) -> Result<(usize, usize)> {
- let after = self.usize_of("after-context")?.unwrap_or(0);
- let before = self.usize_of("before-context")?.unwrap_or(0);
- let both = self.usize_of("context")?.unwrap_or(0);
- Ok(if both > 0 {
- (both, both)
- } else {
- (before, after)
- })
- }
-
- /// Returns the unescaped context separator in UTF-8 bytes.
- ///
- /// If one was not provided, the default `--` is returned.
- fn context_separator(&self) -> Vec<u8> {
- match self.value_of_lossy("context-separator") {
- None => b"--".to_vec(),
- Some(sep) => unescape(&sep),
- }
- }
-
- /// Returns whether the -c/--count or the --count-matches flags were
- /// passed from the command line.
- ///
- /// If --count-matches and --invert-match were passed in, behave
- /// as if --count and --invert-match were passed in (i.e. rg will
- /// count inverted matches as per existing behavior).
- fn counts(&self) -> (bool, bool) {
- let count = self.is_present("count");
- let count_matches = self.is_present("count-matches");
- let invert_matches = self.is_present("invert-match");
- let only_matching = self.is_present("only-matching");
- if count_matches && invert_matches {
- // Treat `-v --count-matches` as `-v -c`.
- (true, false)
- } else if count && only_matching {
- // Treat `-c --only-matching` as `--count-matches`.
- (false, true)
- } else {
- (count, count_matches)
- }
- }
-
- /// Parse the dfa-size-limit argument option into a byte count.
- fn dfa_size_limit(&self) -> Result<Option<usize>> {
- let r = self.parse_human_readable_size("dfa-size-limit")?;
- u64_to_usize("dfa-size-limit", r)
- }
-
- /// Returns the type of encoding to use.
- ///
- /// This only returns an encoding if one is explicitly specified. When no
- /// encoding is present, the Searcher will still do BOM sniffing for UTF-16
- /// and transcode seamlessly.
- fn encoding(&self) -> Result<Option<Encoding>> {
- let label = match self.value_of_lossy("encoding") {
- None => return Ok(None),
- Some(label) => label,
- };
- if label == "auto" {
- return Ok(None);
- }
- Ok(Some(Encoding::new(&label)?))
- }
-
- /// Return the file separator to use based on the CLI configuration.
- fn file_separator(&self) -> Result<Option<Vec<u8>>> {
- // File separators are only used for the standard grep-line format.
- if self.output_kind() != OutputKind::Standard {
- return Ok(None);
- }
-
- let (ctx_before, ctx_after) = self.contexts()?;
- Ok(if self.heading() {
- Some(b"".to_vec())
- } else if ctx_before > 0 || ctx_after > 0 {
- Some(self.context_separator().clone())
- } else {
- None
- })
- }
-
-
- /// Returns true if and only if matches should be grouped with file name
- /// headings.
- fn heading(&self) -> bool {
- if self.is_present("no-heading") || self.is_present("vimgrep") {
- false
- } else {
- atty::is(atty::Stream::Stdout)
- || self.is_present("heading")
- || self.is_present("pretty")
- }
- }
-
- /// Returns true if and only if hidden files/directories should be
- /// searched.
- fn hidden(&self) -> bool {
- self.is_present("hidden") || self.unrestricted_count() >= 2
- }
-
- /// Return all of the ignore file paths given on the command line.
- fn ignore_paths(&self) -> Vec<PathBuf> {
- let paths = match self.values_of_os("ignore-file") {
- None => return vec![],
- Some(paths) => paths,
- };
- paths.map(|p| Path::new(p).to_path_buf()).collect()
- }
-
- /// Returns true if and only if ripgrep is invoked in a way where it knows
- /// it search exactly one thing.
- fn is_one_search(&self, paths: &[PathBuf]) -> bool {
- if paths.len() != 1 {
- return false;
- }
- self.is_only_stdin(paths) || paths[0].is_file()
- }
-
- /// Returns true if and only if we're only searching a single thing and
- /// that thing is stdin.
- fn is_only_stdin(&self, paths: &[PathBuf]) -> bool {
- paths == [Path::new("-")]
- }
-
- /// Returns true if and only if we should show line numbers.
- fn line_number(&self, paths: &[PathBuf]) -> bool {
- if self.output_kind() == OutputKind::Summary {
- return false;
- }
- if self.is_present("no-line-number") {
- return false;
- }
-
- // A few things can imply counting line numbers. In particular, we
- // generally want to show line numbers by default when printing to a
- // tty for human consumption, except for one interesting case: when
- // we're only searching stdin. This makes pipelines work as expected.
- (atty::is(atty::Stream::Stdout) && !self.is_only_stdin(paths))
- || self.is_present("line-number")
- || self.is_present("column")
- || self.is_present("pretty")
- || self.is_present("vimgrep")
- }
-
- /// The maximum number of columns allowed on each line.
- ///
- /// If `0` is provided, then this returns `None`.
- fn max_columns(&self) -> Result<Option<u64>> {
- Ok(self.usize_of_nonzero("max-columns")?.map(|n| n as u64))
- }
-
- /// The maximum number of matches permitted.
- fn max_count(&self) -> Result<Option<u64>> {
- Ok(self.usize_of("max-count")?.map(|n| n as u64))
- }
-
- /// Parses the max-filesize argument option into a byte count.
- fn max_file_size(&self) -> Result<Option<u64>> {
- self.parse_human_readable_size("max-filesize")
- }
-
- /// Returns whether we should attempt to use memory maps or not.
- fn mmap_choice(&self, paths: &[PathBuf]) -> MmapChoice {
- // SAFETY: Memory maps are difficult to impossible to encapsulate
- // safely in a portable way that doesn't simultaneously negate some of
- // the benfits of using memory maps. For ripgrep's use, we never mutate
- // a memory map and generally never store the contents of memory map
- // in a data structure that depends on immutability. Generally
- // speaking, the worst thing that can happen is a SIGBUS (if the
- // underlying file is truncated while reading it), which will cause
- // ripgrep to abort.
- let maybe = unsafe { MmapChoice::auto() };
- let never = MmapChoice::never();
- if self.is_present("no-mmap") {
- never
- } else if self.is_present("mmap") {
- maybe
- } else if paths.len() <= 10 && paths.iter().all(|p| p.is_file()) {
- // If we're only searching a few paths and all of them are
- // files, then memory maps are probably faster.
- maybe
- } else {
- never
- }
- }
-
- /// Returns true if ignore files should be ignored.
- fn no_ignore(&self) -> bool {
- self.is_present("no-ignore") || self.unrestricted_count() >= 1
- }
-
- /// Returns true if global ignore files should be ignored.
- fn no_ignore_global(&self) -> bool {
- self.is_present("no-ignore-global") || self.no_ignore()
- }
-
- /// Returns true if parent ignore files should be ignored.
- fn no_ignore_parent(&self) -> bool {
- self.is_present("no-ignore-parent") || self.no_ignore()
- }
-
- /// Returns true if VCS ignore files should be ignored.
- fn no_ignore_vcs(&self) -> bool {
- self.is_present("no-ignore-vcs") || self.no_ignore()
- }
-
- /// Determine the type of output we should produce.
- fn output_kind(&self) -> OutputKind {
- let (count, count_matches) = self.counts();
- let summary =
- count
- || count_matches
- || self.is_present("files-with-matches")
- || self.is_present("files-without-match")
- || self.is_present("quiet");
- if summary {
- OutputKind::Summary
- } else {
- OutputKind::Standard
- }
- }
-
- /// Builds the set of glob overrides from the command line flags.
- fn overrides(&self) -> Result<Override> {
- let mut builder = OverrideBuilder::new(env::current_dir()?);
- for glob in self.values_of_lossy_vec("glob") {
- builder.add(&glob)?;
- }
- // This only enables case insensitivity for subsequent globs.
- builder.case_insensitive(true)?;
- for glob in self.values_of_lossy_vec("iglob") {
- builder.add(&glob)?;
- }
- Ok(builder.build()?)
- }
-
- /// Return all file paths that ripgrep should search.
- ///
- /// If no paths were given, then this returns an empty list.
- fn paths(&self) -> Vec<PathBuf> {
- let mut paths: Vec<PathBuf> = match self.values_of_os("path") {
- None => vec![],
- Some(paths) => paths.map(|p| Path::new(p).to_path_buf()).collect(),
- };
- // If --file, --files or --regexp is given, then the first path is
- // always in `pattern`.
- if self.is_present("file")
- || self.is_present("files")
- || self.is_present("regexp")
- {
- if let Some(path) = self.value_of_os("pattern") {
- paths.insert(0, Path::new(path).to_path_buf());
- }
- }
- paths
- }
-
- /// Return the default path that ripgrep should search. This should only
- /// be used when ripgrep is not otherwise given at least one file path
- /// as a positional argument.
- fn path_default(&self) -> PathBuf {
- let file_is_stdin = self.values_of_os("file")
- .map_or(false, |mut files| files.any(|f| f == "-"));
- let search_cwd =
- atty::is(atty::Stream::Stdin)
- || !stdin_is_readable()
- || (self.is_present("file") && file_is_stdin)
- || self.is_present("files")
- || self.is_present("type-list");
- if search_cwd {
- Path::new("./").to_path_buf()
- } else {
- Path::new("-").to_path_buf()
- }
- }
-
- /// Returns the unescaped path separator as a single byte, if one exists.
- ///
- /// If the provided path separator is more than a single byte, then an
- /// error is returned.
- fn path_separator(&self) -> Result<Option<u8>> {
- let sep = match self.value_of_lossy("path-separator") {
- None => return Ok(None),
- Some(sep) => unescape(&sep),
- };
- if sep.is_empty() {
- Ok(None)
- } else if sep.len() > 1 {
- Err(From::from(format!(
- "A path separator must be exactly one byte, but \
- the given separator is {} bytes: {}\n\
- In some shells on Windows '/' is automatically \
- expanded. Use '//' instead.",
- sep.len(),
- escape(&a