summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Huang <anthony.huang@affirm.com>2021-05-25 21:41:11 -0400
committerAndrew Gallant <jamslam@gmail.com>2021-05-31 21:51:18 -0400
commit578e1992fa21d3612d3e613d8869dbbb16e33cbe (patch)
treed87e8e2aee7e4d0ad393b5d97023b6b6bd5b7da6
parent46d0130597a8340c19fdd41b107c209653451698 (diff)
cli: add --field-{context,match}-separator flags
These flags permit configuring the bytes used to delimit fields in match or context lines, where "fields" are things like the file path, line number, column number and the match/context itself. Fixes #1842, Closes #1871
-rw-r--r--CHANGELOG.md2
-rw-r--r--complete/_rg2
-rw-r--r--crates/core/app.rs34
-rw-r--r--crates/core/args.rs22
-rw-r--r--tests/feature.rs93
5 files changed, 151 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8c88cd1..4f9a0992 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,8 @@ Feature enhancements:
Cargo will now produce static executables on Windows when using MSVC.
* [FEATURE #1680](https://github.com/BurntSushi/ripgrep/pull/1680):
Add `-.` as a short flag alias for `--hidden`.
+* [FEATURE #1842](https://github.com/BurntSushi/ripgrep/issues/1842):
+ Add `--field-{context,match}-separator` for customizing field delimiters.
* [FEATURE #1856](https://github.com/BurntSushi/ripgrep/pull/1856):
The README now links to a
[Spanish translation](https://github.com/UltiRequiem/traducciones/tree/master/ripgrep).
diff --git a/complete/_rg b/complete/_rg
index 73ec6c6d..31bc697c 100644
--- a/complete/_rg
+++ b/complete/_rg
@@ -303,6 +303,8 @@ _rg() {
'--context-separator=[specify string used to separate non-continuous context lines in output]:separator'
$no"--no-context-separator[don't print context separators]"
'--debug[show debug messages]'
+ '--field-context-separator[set string to delimit fields in context lines]'
+ '--field-match-separator[set string to delimit fields in matching lines]'
'--trace[show more verbose debug messages]'
'--dfa-size-limit=[specify upper size limit of generated DFA]:DFA size (bytes)'
"(1 stats)--files[show each file that would be searched (but don't search)]"
diff --git a/crates/core/app.rs b/crates/core/app.rs
index dd9ae2cd..5058c0ea 100644
--- a/crates/core/app.rs
+++ b/crates/core/app.rs
@@ -568,6 +568,8 @@ pub fn all_args_and_flags() -> Vec<RGArg> {
flag_dfa_size_limit(&mut args);
flag_encoding(&mut args);
flag_engine(&mut args);
+ flag_field_context_separator(&mut args);
+ flag_field_match_separator(&mut args);
flag_file(&mut args);
flag_files(&mut args);
flag_files_with_matches(&mut args);
@@ -1231,6 +1233,38 @@ This overrides previous uses of --pcre2 and --auto-hybrid-regex flags.
args.push(arg);
}
+fn flag_field_context_separator(args: &mut Vec<RGArg>) {
+ const SHORT: &str = "Set the field context separator.";
+ const LONG: &str = long!(
+ "\
+Set the field context separator, which is used to delimit file paths, line
+numbers, columns and the context itself, when printing contextual lines. The
+separator may be any number of bytes, including zero. Escape sequences like
+\\x7F or \\t may be used. The default value is -.
+"
+ );
+ let arg = RGArg::flag("field-context-separator", "SEPARATOR")
+ .help(SHORT)
+ .long_help(LONG);
+ args.push(arg);
+}
+
+fn flag_field_match_separator(args: &mut Vec<RGArg>) {
+ const SHORT: &str = "Set the match separator.";
+ const LONG: &str = long!(
+ "\
+Set the field match separator, which is used to delimit file paths, line
+numbers, columns and the match itself. The separator may be any number of
+bytes, including zero. Escape sequences like \\x7F or \\t may be used. The
+default value is -.
+"
+ );
+ let arg = RGArg::flag("field-match-separator", "SEPARATOR")
+ .help(SHORT)
+ .long_help(LONG);
+ args.push(arg);
+}
+
fn flag_file(args: &mut Vec<RGArg>) {
const SHORT: &str = "Search for patterns from the given file.";
const LONG: &str = long!(
diff --git a/crates/core/args.rs b/crates/core/args.rs
index 7a448362..caa378bd 100644
--- a/crates/core/args.rs
+++ b/crates/core/args.rs
@@ -786,8 +786,8 @@ impl ArgMatches {
.trim_ascii(self.is_present("trim"))
.separator_search(None)
.separator_context(self.context_separator())
- .separator_field_match(b":".to_vec())
- .separator_field_context(b"-".to_vec())
+ .separator_field_match(self.field_match_separator())
+ .separator_field_context(self.field_context_separator())
.separator_path(self.path_separator()?)
.path_terminator(self.path_terminator());
if separator_search {
@@ -1377,6 +1377,24 @@ impl ArgMatches {
}
}
+ /// Returns the unescaped field context separator. If one wasn't specified,
+ /// then '-' is used as the default.
+ fn field_context_separator(&self) -> Vec<u8> {
+ match self.value_of_os("field-context-separator") {
+ None => b"-".to_vec(),
+ Some(sep) => cli::unescape_os(&sep),
+ }
+ }
+
+ /// Returns the unescaped field match separator. If one wasn't specified,
+ /// then ':' is used as the default.
+ fn field_match_separator(&self) -> Vec<u8> {
+ match self.value_of_os("field-match-separator") {
+ None => b":".to_vec(),
+ Some(sep) => cli::unescape_os(&sep),
+ }
+ }
+
/// Get a sequence of all available patterns from the command line.
/// This includes reading the -e/--regexp and -f/--file flags.
///
diff --git a/tests/feature.rs b/tests/feature.rs
index 3cae7075..36cbad73 100644
--- a/tests/feature.rs
+++ b/tests/feature.rs
@@ -828,6 +828,99 @@ rgtest!(f1404_nothing_searched_ignored, |dir: Dir, mut cmd: TestCommand| {
eqnice!(expected, stderr);
});
+// See: https://github.com/BurntSushi/ripgrep/issues/1842
+rgtest!(f1842_field_context_separator, |dir: Dir, _: TestCommand| {
+ dir.create("sherlock", SHERLOCK);
+
+ // Test the default.
+ let base = &["-n", "-A1", "Doctor Watsons", "sherlock"];
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+2-Holmeses, success in the province of detective work must always
+";
+ eqnice!(expected, dir.command().args(base).stdout());
+
+ // Test that it can be overridden.
+ let mut args = vec!["--field-context-separator", "!"];
+ args.extend(base);
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+2!Holmeses, success in the province of detective work must always
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that it can use multiple bytes.
+ let mut args = vec!["--field-context-separator", "!!"];
+ args.extend(base);
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+2!!Holmeses, success in the province of detective work must always
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that unescaping works.
+ let mut args = vec!["--field-context-separator", r"\x7F"];
+ args.extend(base);
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+2\x7FHolmeses, success in the province of detective work must always
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that an empty separator is OK.
+ let mut args = vec!["--field-context-separator", r""];
+ args.extend(base);
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+2Holmeses, success in the province of detective work must always
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+});
+
+// See: https://github.com/BurntSushi/ripgrep/issues/1842
+rgtest!(f1842_field_match_separator, |dir: Dir, _: TestCommand| {
+ dir.create("sherlock", SHERLOCK);
+
+ // Test the default.
+ let base = &["-n", "Doctor Watsons", "sherlock"];
+ let expected = "\
+1:For the Doctor Watsons of this world, as opposed to the Sherlock
+";
+ eqnice!(expected, dir.command().args(base).stdout());
+
+ // Test that it can be overridden.
+ let mut args = vec!["--field-match-separator", "!"];
+ args.extend(base);
+ let expected = "\
+1!For the Doctor Watsons of this world, as opposed to the Sherlock
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that it can use multiple bytes.
+ let mut args = vec!["--field-match-separator", "!!"];
+ args.extend(base);
+ let expected = "\
+1!!For the Doctor Watsons of this world, as opposed to the Sherlock
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that unescaping works.
+ let mut args = vec!["--field-match-separator", r"\x7F"];
+ args.extend(base);
+ let expected = "\
+1\x7FFor the Doctor Watsons of this world, as opposed to the Sherlock
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+
+ // Test that an empty separator is OK.
+ let mut args = vec!["--field-match-separator", r""];
+ args.extend(base);
+ let expected = "\
+1For the Doctor Watsons of this world, as opposed to the Sherlock
+";
+ eqnice!(expected, dir.command().args(&args).stdout());
+});
+
rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| {
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
cmd.args(&["-A1", "--no-context-separator", "foo", "test"]);