1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-06-20 06:15:37 +02:00

printer: add hyperlinks

This commit represents the initial work to get hyperlinks working and
was submitted as part of PR #2483. Subsequent commits largely retain the
functionality and structure of the hyperlink support added here, but
rejigger some things around.
This commit is contained in:
Lucas Trzesniewski
2023-07-08 00:56:50 +02:00
committed by Andrew Gallant
parent 86ef683308
commit 1a50324013
16 changed files with 1178 additions and 83 deletions

View File

@ -10,6 +10,7 @@ use termcolor::{ColorSpec, NoColor, WriteColor};
use crate::color::ColorSpecs;
use crate::counter::CounterWriter;
use crate::hyperlink::{HyperlinkPattern, HyperlinkSpan};
use crate::stats::Stats;
use crate::util::{find_iter_at_in_context, PrinterPath};
@ -22,6 +23,7 @@ use crate::util::{find_iter_at_in_context, PrinterPath};
struct Config {
kind: SummaryKind,
colors: ColorSpecs,
hyperlink_pattern: HyperlinkPattern,
stats: bool,
path: bool,
max_matches: Option<u64>,
@ -36,6 +38,7 @@ impl Default for Config {
Config {
kind: SummaryKind::Count,
colors: ColorSpecs::default(),
hyperlink_pattern: HyperlinkPattern::default(),
stats: false,
path: true,
max_matches: None,
@ -160,6 +163,7 @@ impl SummaryBuilder {
Summary {
config: self.config.clone(),
wtr: RefCell::new(CounterWriter::new(wtr)),
buf: vec![],
}
}
@ -206,6 +210,17 @@ impl SummaryBuilder {
self
}
/// Set the hyperlink pattern to use for hyperlinks output by this printer.
///
/// Colors need to be enabled for hyperlinks to be output.
pub fn hyperlink_pattern(
&mut self,
pattern: HyperlinkPattern,
) -> &mut SummaryBuilder {
self.config.hyperlink_pattern = pattern;
self
}
/// Enable the gathering of various aggregate statistics.
///
/// When this is enabled (it's disabled by default), statistics will be
@ -328,6 +343,7 @@ impl SummaryBuilder {
pub struct Summary<W> {
config: Config,
wtr: RefCell<CounterWriter<W>>,
buf: Vec<u8>,
}
impl<W: WriteColor> Summary<W> {
@ -532,12 +548,9 @@ impl<'p, 's, M: Matcher, W: WriteColor> SummarySink<'p, 's, M, W> {
/// write that path to the underlying writer followed by a line terminator.
/// (If a path terminator is set, then that is used instead of the line
/// terminator.)
fn write_path_line(&self, searcher: &Searcher) -> io::Result<()> {
if let Some(ref path) = self.path {
self.write_spec(
self.summary.config.colors.path(),
path.as_bytes(),
)?;
fn write_path_line(&mut self, searcher: &Searcher) -> io::Result<()> {
if self.path.is_some() {
self.write_path()?;
if let Some(term) = self.summary.config.path_terminator {
self.write(&[term])?;
} else {
@ -551,12 +564,9 @@ impl<'p, 's, M: Matcher, W: WriteColor> SummarySink<'p, 's, M, W> {
/// write that path to the underlying writer followed by the field
/// separator. (If a path terminator is set, then that is used instead of
/// the field separator.)
fn write_path_field(&self) -> io::Result<()> {
if let Some(ref path) = self.path {
self.write_spec(
self.summary.config.colors.path(),
path.as_bytes(),
)?;
fn write_path_field(&mut self) -> io::Result<()> {
if self.path.is_some() {
self.write_path()?;
if let Some(term) = self.summary.config.path_terminator {
self.write(&[term])?;
} else {
@ -566,6 +576,43 @@ impl<'p, 's, M: Matcher, W: WriteColor> SummarySink<'p, 's, M, W> {
Ok(())
}
/// If this printer has a file path associated with it, then this will
/// write that path to the underlying writer in the appropriate style
/// (color and hyperlink).
fn write_path(&mut self) -> io::Result<()> {
if self.path.is_some() {
let mut hyperlink = self.start_hyperlink_span()?;
self.write_spec(
self.summary.config.colors.path(),
self.path.as_ref().unwrap().as_bytes(),
)?;
if hyperlink.is_active() {
hyperlink.end(&mut *self.summary.wtr.borrow_mut())?;
}
}
Ok(())
}
/// Starts a hyperlink span when applicable.
fn start_hyperlink_span(&mut self) -> io::Result<HyperlinkSpan> {
if let Some(ref path) = self.path {
let mut wtr = self.summary.wtr.borrow_mut();
if wtr.supports_hyperlinks() {
if let Some(spec) = path.create_hyperlink_spec(
&self.summary.config.hyperlink_pattern,
None,
None,
&mut self.summary.buf,
) {
return Ok(HyperlinkSpan::start(&mut *wtr, &spec)?);
}
}
}
Ok(HyperlinkSpan::default())
}
/// Write the line terminator configured on the given searcher.
fn write_line_term(&self, searcher: &Searcher) -> io::Result<()> {
self.write(searcher.line_terminator().as_bytes())
@ -704,11 +751,11 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
}
SummaryKind::CountMatches => {
if show_count {
self.write_path_field()?;
let stats = self
.stats
.as_ref()
.expect("CountMatches should enable stats tracking");
self.write_path_field()?;
self.write(stats.matches().to_string().as_bytes())?;
self.write_line_term(searcher)?;
}