1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-10-06 05:36:58 +02:00

colors: add highlight type support for matching lines

This lets users highlight non-matching text in matching lines.

Closes #3024, Closes #3107
This commit is contained in:
emrebengue
2025-07-23 21:45:17 -04:00
committed by Andrew Gallant
parent 126bbeab8c
commit 99fe884536
6 changed files with 88 additions and 17 deletions

View File

@@ -43,6 +43,8 @@ Feature enhancements:
When using multithreading, schedule files to search in order given on CLI.
* [FEATURE #2943](https://github.com/BurntSushi/ripgrep/issues/2943):
Add `aarch64` release artifacts for Windows.
* [FEATURE #3024](https://github.com/BurntSushi/ripgrep/issues/3024):
Add `highlight` color type, for styling non-matching text in a matching line.
* [FEATURE #3048](https://github.com/BurntSushi/ripgrep/pull/3048):
Globs in ripgrep (and the `globset` crate) now support nested alternates.

4
FAQ.md
View File

@@ -285,8 +285,8 @@ As a special case, `--colors '{type}:none'` will clear all colors and styles
associated with `{type}`, which lets you start with a clean slate (instead of
building on top of ripgrep's default color settings).
Here's an example that makes highlights the matches with a nice blue background
with bolded white text:
Here's an example that highlights the matches with a nice blue background with
bolded white text:
```
$ rg somepattern \

View File

@@ -363,10 +363,11 @@ _rg() {
'column:specify coloring for column numbers'
'line:specify coloring for line numbers'
'match:specify coloring for match text'
'highlight:specify coloring for matching lines'
'path:specify coloring for file names'
)
descr='color/style type'
elif [[ ${IPREFIX#--*=}$PREFIX == (column|line|match|path):[^:]# ]]; then
elif [[ ${IPREFIX#--*=}$PREFIX == (column|line|match|highlight|path):[^:]# ]]; then
suf=( -qS: )
tmp=(
'none:clear color/style for type'

View File

@@ -751,7 +751,8 @@ the \flag{colors} flag to manually set all color styles to \fBnone\fP:
\-\-colors 'path:none' \\
\-\-colors 'line:none' \\
\-\-colors 'column:none' \\
\-\-colors 'match:none'
\-\-colors 'match:none' \\
\-\-colors 'highlight:none'
.EE
.sp
"
@@ -829,7 +830,7 @@ impl Flag for Colors {
"Configure color settings and styles."
}
fn doc_long(&self) -> &'static str {
r"
r#"
This flag specifies color settings for use in the output. This flag may be
provided multiple times. Settings are applied iteratively. Pre-existing color
labels are limited to one of eight choices: \fBred\fP, \fBblue\fP, \fBgreen\fP,
@@ -839,11 +840,11 @@ are limited to \fBnobold\fP, \fBbold\fP, \fBnointense\fP, \fBintense\fP,
.sp
The format of the flag is
\fB{\fP\fItype\fP\fB}:{\fP\fIattribute\fP\fB}:{\fP\fIvalue\fP\fB}\fP.
\fItype\fP should be one of \fBpath\fP, \fBline\fP, \fBcolumn\fP or
\fBmatch\fP. \fIattribute\fP can be \fBfg\fP, \fBbg\fP or \fBstyle\fP.
\fIvalue\fP is either a color (for \fBfg\fP and \fBbg\fP) or a text style. A
special format, \fB{\fP\fItype\fP\fB}:none\fP, will clear all color settings
for \fItype\fP.
\fItype\fP should be one of \fBpath\fP, \fBline\fP, \fBcolumn\fP,
\fBhighlight\fP or \fBmatch\fP. \fIattribute\fP can be \fBfg\fP, \fBbg\fP or
\fBstyle\fP. \fIvalue\fP is either a color (for \fBfg\fP and \fBbg\fP) or a
text style. A special format, \fB{\fP\fItype\fP\fB}:none\fP, will clear all
color settings for \fItype\fP.
.sp
For example, the following command will change the match color to magenta and
the background color for line numbers to yellow:
@@ -852,6 +853,17 @@ the background color for line numbers to yellow:
rg \-\-colors 'match:fg:magenta' \-\-colors 'line:bg:yellow'
.EE
.sp
Another example, the following command will "highlight" the non-matching text
in matching lines:
.sp
.EX
rg \-\-colors 'highlight:bg:yellow' \-\-colors 'highlight:fg:black'
.EE
.sp
The "highlight" color type is particularly useful for contrasting matching
lines with surrounding context printed by the \flag{before-context},
\flag{after-context}, \flag{context} or \flag{passthru} flags.
.sp
Extended colors can be used for \fIvalue\fP when the tty supports ANSI color
sequences. These are specified as either \fIx\fP (256-color) or
.IB x , x , x
@@ -874,7 +886,7 @@ or, equivalently,
.sp
Note that the \fBintense\fP and \fBnointense\fP styles will have no effect when
used alongside these extended color codes.
"
"#
}
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
@@ -908,6 +920,24 @@ fn test_colors() {
"line:bg:yellow".parse().unwrap()
]
);
let args = parse_low_raw(["--colors", "highlight:bg:240"]).unwrap();
assert_eq!(args.colors, vec!["highlight:bg:240".parse().unwrap()]);
let args = parse_low_raw([
"--colors",
"match:fg:magenta",
"--colors",
"highlight:bg:blue",
])
.unwrap();
assert_eq!(
args.colors,
vec![
"match:fg:magenta".parse().unwrap(),
"highlight:bg:blue".parse().unwrap()
]
);
}
/// --column

View File

@@ -51,13 +51,13 @@ impl std::fmt::Display for ColorError {
ColorError::UnrecognizedOutType(ref name) => write!(
f,
"unrecognized output type '{}'. Choose from: \
path, line, column, match.",
path, line, column, match, highlight.",
name,
),
ColorError::UnrecognizedSpecType(ref name) => write!(
f,
"unrecognized spec type '{}'. Choose from: \
fg, bg, style, none.",
fg, bg, style, none.",
name,
),
ColorError::UnrecognizedColor(_, ref msg) => write!(f, "{}", msg),
@@ -70,8 +70,8 @@ impl std::fmt::Display for ColorError {
),
ColorError::InvalidFormat(ref original) => write!(
f,
"invalid color spec format: '{}'. Valid format \
is '(path|line|column|match):(fg|bg|style):(value)'.",
"invalid color spec format: '{}'. Valid format is \
'(path|line|column|match|highlight):(fg|bg|style):(value)'.",
original,
),
}
@@ -90,6 +90,7 @@ pub struct ColorSpecs {
line: ColorSpec,
column: ColorSpec,
matched: ColorSpec,
highlight: ColorSpec,
}
/// A single color specification provided by the user.
@@ -99,7 +100,7 @@ pub struct ColorSpecs {
/// The format of a `Spec` is a triple: `{type}:{attribute}:{value}`. Each
/// component is defined as follows:
///
/// * `{type}` can be one of `path`, `line`, `column` or `match`.
/// * `{type}` can be one of `path`, `line`, `column`, `match` or `highlight`.
/// * `{attribute}` can be one of `fg`, `bg` or `style`. `{attribute}` may also
/// be the special value `none`, in which case, `{value}` can be omitted.
/// * `{value}` is either a color name (for `fg`/`bg`) or a style instruction.
@@ -181,6 +182,7 @@ enum OutType {
Line,
Column,
Match,
Highlight,
}
/// The specification type.
@@ -216,6 +218,7 @@ impl ColorSpecs {
OutType::Line => spec.merge_into(&mut merged.line),
OutType::Column => spec.merge_into(&mut merged.column),
OutType::Match => spec.merge_into(&mut merged.matched),
OutType::Highlight => spec.merge_into(&mut merged.highlight),
}
}
merged
@@ -249,6 +252,12 @@ impl ColorSpecs {
pub fn matched(&self) -> &ColorSpec {
&self.matched
}
/// Return the color specification for coloring entire line if there is a
/// matched text.
pub fn highlight(&self) -> &ColorSpec {
&self.highlight
}
}
impl UserColorSpec {
@@ -348,6 +357,7 @@ impl std::str::FromStr for OutType {
"line" => Ok(OutType::Line),
"column" => Ok(OutType::Column),
"match" => Ok(OutType::Match),
"highlight" => Ok(OutType::Highlight),
_ => Err(ColorError::UnrecognizedOutType(s.to_string())),
}
}

View File

@@ -1320,6 +1320,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.write(&bytes[line])?;
return Ok(());
}
self.start_line_highlight()?;
while !line.is_empty() {
if matches[*match_index].end() <= line.start() {
if *match_index + 1 < matches.len() {
@@ -1346,6 +1347,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
}
}
self.end_color_match()?;
self.end_line_highlight()?;
Ok(())
}
@@ -1547,11 +1549,37 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
if !self.in_color_match.get() {
return Ok(());
}
self.wtr().borrow_mut().reset()?;
if self.highlight_on() {
self.wtr()
.borrow_mut()
.set_color(self.config().colors.highlight())?;
} else {
self.wtr().borrow_mut().reset()?;
}
self.in_color_match.set(false);
Ok(())
}
fn highlight_on(&self) -> bool {
!self.config().colors.highlight().is_none() && !self.is_context()
}
fn start_line_highlight(&self) -> io::Result<()> {
if self.highlight_on() {
self.wtr()
.borrow_mut()
.set_color(self.config().colors.highlight())?;
}
Ok(())
}
fn end_line_highlight(&self) -> io::Result<()> {
if self.highlight_on() {
self.wtr().borrow_mut().reset()?;
}
Ok(())
}
fn write(&self, buf: &[u8]) -> io::Result<()> {
self.wtr().borrow_mut().write_all(buf)
}