From 8751e5570697e6b2b913b2ab06e0dabf9f28b436 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Tue, 10 Jan 2017 18:16:15 -0500 Subject: [PATCH] Add --path-separator flag. This flag permits setting the path separator used for all file paths printed by ripgrep in normal operation. Fixes #275 --- doc/rg.1 | 12 +++++++++++- doc/rg.1.md | 8 +++++++- src/app.rs | 11 ++++++++++- src/args.rs | 22 ++++++++++++++++++++++ src/printer.rs | 31 +++++++++++++++++++++++++++++-- tests/tests.rs | 10 ++++++++++ 6 files changed, 89 insertions(+), 5 deletions(-) diff --git a/doc/rg.1 b/doc/rg.1 index 95539939..bed7ac65 100644 --- a/doc/rg.1 +++ b/doc/rg.1 @@ -186,7 +186,7 @@ One byte is equal to one column. .RS .RE .TP -.B \-\-context\-separator \f[I]ARG\f[] +.B \-\-context\-separator \f[I]SEPARATOR\f[] The string to use when separating non\-continuous context lines. Escape sequences may be used. [default: \-\-] @@ -328,6 +328,16 @@ and \-\-files. .RS .RE .TP +.B \-\-path\-separator \f[I]SEPARATOR\f[] +The path separator to use when printing file paths. +This defaults to your platform\[aq]s path separator, which is / on Unix +and \\ on Windows. +This flag is intended for overriding the default when the environment +demands it (e.g., cygwin). +A path separator is limited to a single byte. +.RS +.RE +.TP .B \-p, \-\-pretty Alias for \-\-color=always \-\-heading \-n. .RS diff --git a/doc/rg.1.md b/doc/rg.1.md index 7f19cecf..5e0f7031 100644 --- a/doc/rg.1.md +++ b/doc/rg.1.md @@ -128,7 +128,7 @@ Project home page: https://github.com/BurntSushi/ripgrep numbers for the first match on each line. Note that this doesn't try to account for Unicode. One byte is equal to one column. ---context-separator *ARG* +--context-separator *SEPARATOR* : The string to use when separating non-continuous context lines. Escape sequences may be used. [default: --] @@ -221,6 +221,12 @@ Project home page: https://github.com/BurntSushi/ripgrep a list of matching files such as with --count, --files-with-matches and --files. +--path-separator *SEPARATOR* +: The path separator to use when printing file paths. This defaults to your + platform's path separator, which is / on Unix and \\ on Windows. This flag is + intended for overriding the default when the environment demands it (e.g., + cygwin). A path separator is limited to a single byte. + -p, --pretty : Alias for --color=always --heading -n. diff --git a/src/app.rs b/src/app.rs index a9dd06a6..a353fdb9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -125,7 +125,8 @@ fn app(next_line_help: bool, doc: F) -> App<'static, 'static> .value_name("NUM").takes_value(true) .validator(validate_number)) .arg(flag("column")) - .arg(flag("context-separator").value_name("ARG").takes_value(true)) + .arg(flag("context-separator") + .value_name("SEPARATOR").takes_value(true)) .arg(flag("debug")) .arg(flag("file").short("f") .value_name("FILE").takes_value(true) @@ -154,6 +155,7 @@ fn app(next_line_help: bool, doc: F) -> App<'static, 'static> .arg(flag("no-ignore-parent")) .arg(flag("no-ignore-vcs")) .arg(flag("null")) + .arg(flag("path-separator").value_name("SEPARATOR").takes_value(true)) .arg(flag("pretty").short("p")) .arg(flag("replace").short("r").value_name("ARG").takes_value(true)) .arg(flag("case-sensitive").short("s")) @@ -410,6 +412,13 @@ lazy_static! { printing a list of matching files such as with --count, \ --files-with-matches and --files. This option is useful for use \ with xargs."); + doc!(h, "path-separator", + "Path separator to use when printing file paths.", + "The path separator to use when printing file paths. This \ + defaults to your platform's path separator, which is / on Unix \ + and \\ on Windows. This flag is intended for overriding the \ + default when the environment demands it (e.g., cygwin). A path \ + separator is limited to a single byte."); doc!(h, "pretty", "Alias for --color always --heading -n."); doc!(h, "replace", diff --git a/src/args.rs b/src/args.rs index 29a7fa81..594ece09 100644 --- a/src/args.rs +++ b/src/args.rs @@ -62,6 +62,7 @@ pub struct Args { no_ignore_vcs: bool, no_messages: bool, null: bool, + path_separator: Option, quiet: bool, quiet_matched: QuietMatched, replace: Option>, @@ -151,6 +152,7 @@ impl Args { .heading(self.heading) .line_per_match(self.line_per_match) .null(self.null) + .path_separator(self.path_separator) .with_filename(self.with_filename); if let Some(ref rep) = self.replace { p = p.replace(rep.clone()); @@ -347,6 +349,7 @@ impl<'a> ArgMatches<'a> { no_ignore_vcs: self.no_ignore_vcs(), no_messages: self.is_present("no-messages"), null: self.is_present("null"), + path_separator: try!(self.path_separator()), quiet: quiet, quiet_matched: QuietMatched::new(quiet), replace: self.replace(), @@ -616,6 +619,25 @@ impl<'a> ArgMatches<'a> { } } + /// Returns the unescaped path separator in UTF-8 bytes. + fn path_separator(&self) -> Result> { + match self.value_of_lossy("path-separator") { + None => Ok(None), + Some(sep) => { + let 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.", sep.len()))) + } else { + Ok(Some(sep[0])) + } + } + } + } + /// Returns the before and after contexts from the command line. /// /// If a context setting was absent, then `0` is returned. diff --git a/src/printer.rs b/src/printer.rs index c57e1611..8c04dd1a 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -44,6 +44,8 @@ pub struct Printer { with_filename: bool, /// The color specifications. colors: ColorSpecs, + /// The separator to use for file paths. If empty, this is ignored. + path_separator: Option, } impl Printer { @@ -62,6 +64,7 @@ impl Printer { replace: None, with_filename: false, colors: ColorSpecs::default(), + path_separator: None, } } @@ -118,6 +121,13 @@ impl Printer { self } + /// A separator to use when printing file paths. When empty, use the + /// default separator for the current platform. (/ on Unix, \ on Windows.) + pub fn path_separator(mut self, sep: Option) -> Printer { + self.path_separator = sep; + self + } + /// Replace every match in each matching line with the replacement string /// given. /// @@ -342,12 +352,29 @@ impl Printer { use std::os::unix::ffi::OsStrExt; let path = path.as_ref().as_os_str().as_bytes(); - self.write(path); + match self.path_separator { + None => self.write(path), + Some(sep) => self.write_path_with_sep(path, sep), + } } #[cfg(not(unix))] fn write_path>(&mut self, path: P) { - self.write(path.as_ref().to_string_lossy().as_bytes()); + let path = path.as_ref().to_string_lossy(); + match self.path_separator { + None => self.write(path.as_bytes()), + Some(sep) => self.write_path_with_sep(path.as_bytes(), sep), + } + } + + fn write_path_with_sep(&mut self, path: &[u8], sep: u8) { + let mut path = path.to_vec(); + for b in &mut path { + if *b == b'/' || (cfg!(windows) && *b == b'\\') { + *b = sep; + } + } + self.write(&path); } fn write(&mut self, buf: &[u8]) { diff --git a/tests/tests.rs b/tests/tests.rs index 11a76044..92d8e8ca 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1226,6 +1226,16 @@ clean!(feature_263_sort_files, "test", ".", |wd: WorkDir, mut cmd: Command| { assert_eq!(lines, "abc:test\nbar:test\nfoo:test\nzoo:test\n"); }); +// See: https://github.com/BurntSushi/ripgrep/issues/275 +clean!(feature_275_pathsep, "test", ".", |wd: WorkDir, mut cmd: Command| { + wd.create_dir("foo"); + wd.create("foo/bar", "test"); + cmd.arg("--path-separator").arg("Z"); + + let lines: String = wd.stdout(&mut cmd); + assert_eq!(lines, "fooZbar:test\n"); +}); + #[test] fn binary_nosearch() { let wd = WorkDir::new("binary_nosearch");