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. When using multithreading, schedule files to search in order given on CLI.
* [FEATURE #2943](https://github.com/BurntSushi/ripgrep/issues/2943): * [FEATURE #2943](https://github.com/BurntSushi/ripgrep/issues/2943):
Add `aarch64` release artifacts for Windows. 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): * [FEATURE #3048](https://github.com/BurntSushi/ripgrep/pull/3048):
Globs in ripgrep (and the `globset` crate) now support nested alternates. 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 associated with `{type}`, which lets you start with a clean slate (instead of
building on top of ripgrep's default color settings). building on top of ripgrep's default color settings).
Here's an example that makes highlights the matches with a nice blue background Here's an example that highlights the matches with a nice blue background with
with bolded white text: bolded white text:
``` ```
$ rg somepattern \ $ rg somepattern \

View File

@@ -363,10 +363,11 @@ _rg() {
'column:specify coloring for column numbers' 'column:specify coloring for column numbers'
'line:specify coloring for line numbers' 'line:specify coloring for line numbers'
'match:specify coloring for match text' 'match:specify coloring for match text'
'highlight:specify coloring for matching lines'
'path:specify coloring for file names' 'path:specify coloring for file names'
) )
descr='color/style type' descr='color/style type'
elif [[ ${IPREFIX#--*=}$PREFIX == (column|line|match|path):[^:]# ]]; then elif [[ ${IPREFIX#--*=}$PREFIX == (column|line|match|highlight|path):[^:]# ]]; then
suf=( -qS: ) suf=( -qS: )
tmp=( tmp=(
'none:clear color/style for type' '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 'path:none' \\
\-\-colors 'line:none' \\ \-\-colors 'line:none' \\
\-\-colors 'column:none' \\ \-\-colors 'column:none' \\
\-\-colors 'match:none' \-\-colors 'match:none' \\
\-\-colors 'highlight:none'
.EE .EE
.sp .sp
" "
@@ -829,7 +830,7 @@ impl Flag for Colors {
"Configure color settings and styles." "Configure color settings and styles."
} }
fn doc_long(&self) -> &'static str { fn doc_long(&self) -> &'static str {
r" r#"
This flag specifies color settings for use in the output. This flag may be This flag specifies color settings for use in the output. This flag may be
provided multiple times. Settings are applied iteratively. Pre-existing color provided multiple times. Settings are applied iteratively. Pre-existing color
labels are limited to one of eight choices: \fBred\fP, \fBblue\fP, \fBgreen\fP, 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 .sp
The format of the flag is The format of the flag is
\fB{\fP\fItype\fP\fB}:{\fP\fIattribute\fP\fB}:{\fP\fIvalue\fP\fB}\fP. \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 \fItype\fP should be one of \fBpath\fP, \fBline\fP, \fBcolumn\fP,
\fBmatch\fP. \fIattribute\fP can be \fBfg\fP, \fBbg\fP or \fBstyle\fP. \fBhighlight\fP or \fBmatch\fP. \fIattribute\fP can be \fBfg\fP, \fBbg\fP or
\fIvalue\fP is either a color (for \fBfg\fP and \fBbg\fP) or a text style. A \fBstyle\fP. \fIvalue\fP is either a color (for \fBfg\fP and \fBbg\fP) or a
special format, \fB{\fP\fItype\fP\fB}:none\fP, will clear all color settings text style. A special format, \fB{\fP\fItype\fP\fB}:none\fP, will clear all
for \fItype\fP. color settings for \fItype\fP.
.sp .sp
For example, the following command will change the match color to magenta and For example, the following command will change the match color to magenta and
the background color for line numbers to yellow: 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' rg \-\-colors 'match:fg:magenta' \-\-colors 'line:bg:yellow'
.EE .EE
.sp .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 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 sequences. These are specified as either \fIx\fP (256-color) or
.IB x , x , x .IB x , x , x
@@ -874,7 +886,7 @@ or, equivalently,
.sp .sp
Note that the \fBintense\fP and \fBnointense\fP styles will have no effect when Note that the \fBintense\fP and \fBnointense\fP styles will have no effect when
used alongside these extended color codes. used alongside these extended color codes.
" "#
} }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> { fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
@@ -908,6 +920,24 @@ fn test_colors() {
"line:bg:yellow".parse().unwrap() "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 /// --column

View File

@@ -51,13 +51,13 @@ impl std::fmt::Display for ColorError {
ColorError::UnrecognizedOutType(ref name) => write!( ColorError::UnrecognizedOutType(ref name) => write!(
f, f,
"unrecognized output type '{}'. Choose from: \ "unrecognized output type '{}'. Choose from: \
path, line, column, match.", path, line, column, match, highlight.",
name, name,
), ),
ColorError::UnrecognizedSpecType(ref name) => write!( ColorError::UnrecognizedSpecType(ref name) => write!(
f, f,
"unrecognized spec type '{}'. Choose from: \ "unrecognized spec type '{}'. Choose from: \
fg, bg, style, none.", fg, bg, style, none.",
name, name,
), ),
ColorError::UnrecognizedColor(_, ref msg) => write!(f, "{}", msg), ColorError::UnrecognizedColor(_, ref msg) => write!(f, "{}", msg),
@@ -70,8 +70,8 @@ impl std::fmt::Display for ColorError {
), ),
ColorError::InvalidFormat(ref original) => write!( ColorError::InvalidFormat(ref original) => write!(
f, f,
"invalid color spec format: '{}'. Valid format \ "invalid color spec format: '{}'. Valid format is \
is '(path|line|column|match):(fg|bg|style):(value)'.", '(path|line|column|match|highlight):(fg|bg|style):(value)'.",
original, original,
), ),
} }
@@ -90,6 +90,7 @@ pub struct ColorSpecs {
line: ColorSpec, line: ColorSpec,
column: ColorSpec, column: ColorSpec,
matched: ColorSpec, matched: ColorSpec,
highlight: ColorSpec,
} }
/// A single color specification provided by the user. /// 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 /// The format of a `Spec` is a triple: `{type}:{attribute}:{value}`. Each
/// component is defined as follows: /// 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 /// * `{attribute}` can be one of `fg`, `bg` or `style`. `{attribute}` may also
/// be the special value `none`, in which case, `{value}` can be omitted. /// 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. /// * `{value}` is either a color name (for `fg`/`bg`) or a style instruction.
@@ -181,6 +182,7 @@ enum OutType {
Line, Line,
Column, Column,
Match, Match,
Highlight,
} }
/// The specification type. /// The specification type.
@@ -216,6 +218,7 @@ impl ColorSpecs {
OutType::Line => spec.merge_into(&mut merged.line), OutType::Line => spec.merge_into(&mut merged.line),
OutType::Column => spec.merge_into(&mut merged.column), OutType::Column => spec.merge_into(&mut merged.column),
OutType::Match => spec.merge_into(&mut merged.matched), OutType::Match => spec.merge_into(&mut merged.matched),
OutType::Highlight => spec.merge_into(&mut merged.highlight),
} }
} }
merged merged
@@ -249,6 +252,12 @@ impl ColorSpecs {
pub fn matched(&self) -> &ColorSpec { pub fn matched(&self) -> &ColorSpec {
&self.matched &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 { impl UserColorSpec {
@@ -348,6 +357,7 @@ impl std::str::FromStr for OutType {
"line" => Ok(OutType::Line), "line" => Ok(OutType::Line),
"column" => Ok(OutType::Column), "column" => Ok(OutType::Column),
"match" => Ok(OutType::Match), "match" => Ok(OutType::Match),
"highlight" => Ok(OutType::Highlight),
_ => Err(ColorError::UnrecognizedOutType(s.to_string())), _ => 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])?; self.write(&bytes[line])?;
return Ok(()); return Ok(());
} }
self.start_line_highlight()?;
while !line.is_empty() { while !line.is_empty() {
if matches[*match_index].end() <= line.start() { if matches[*match_index].end() <= line.start() {
if *match_index + 1 < matches.len() { 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_color_match()?;
self.end_line_highlight()?;
Ok(()) Ok(())
} }
@@ -1547,11 +1549,37 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
if !self.in_color_match.get() { if !self.in_color_match.get() {
return Ok(()); 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); self.in_color_match.set(false);
Ok(()) 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<()> { fn write(&self, buf: &[u8]) -> io::Result<()> {
self.wtr().borrow_mut().write_all(buf) self.wtr().borrow_mut().write_all(buf)
} }