feat(printer): add support for colored output
This commit is contained in:
parent
e69dbb086e
commit
754f79edeb
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -306,6 +306,7 @@ dependencies = [
|
||||
"ignore",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -25,6 +25,7 @@ grep = "0.3.1"
|
||||
ignore = "0.4.22"
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_json = "1.0.120"
|
||||
termcolor = "1.4.1"
|
||||
|
||||
[[bin]]
|
||||
name = "gg"
|
||||
|
@ -40,6 +40,8 @@ Options:
|
||||
-U, --multiline enable multiline matching
|
||||
--json output in JSON format
|
||||
-f, --file-paths-only output file paths only
|
||||
-A, --absolute-paths output absolute paths (defaults to relative)
|
||||
-C, --colored-output toggle colored output (defaults to ON)
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
|
12
src/cli.rs
12
src/cli.rs
@ -40,6 +40,14 @@ pub struct Cli {
|
||||
/// output file paths only
|
||||
#[clap(short = 'f', long, default_value_t = false)]
|
||||
pub file_paths_only: bool,
|
||||
|
||||
/// output absolute paths (defaults to relative)
|
||||
#[clap(short = 'A', long, default_value_t = false)]
|
||||
pub absolute_paths: bool,
|
||||
|
||||
/// toggle colored output (defaults to ON)
|
||||
#[clap(short = 'C', long, default_value_t = true)]
|
||||
pub colored_output: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -52,6 +60,8 @@ pub struct PostProcessedCli {
|
||||
pub respect_gitignore: bool,
|
||||
pub multiline: bool,
|
||||
pub print_mode: PrintMode,
|
||||
pub absolute_paths: bool,
|
||||
pub colored_output: bool,
|
||||
}
|
||||
|
||||
pub fn process_cli_args(cli: Cli) -> anyhow::Result<PostProcessedCli> {
|
||||
@ -70,5 +80,7 @@ pub fn process_cli_args(cli: Cli) -> anyhow::Result<PostProcessedCli> {
|
||||
} else {
|
||||
PrintMode::Text
|
||||
},
|
||||
absolute_paths: cli.absolute_paths,
|
||||
colored_output: cli.colored_output,
|
||||
})
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use clap::Parser;
|
||||
|
||||
use crossbeam::queue::ArrayQueue;
|
||||
use ignore::DirEntry;
|
||||
use printer::PrinterConfig;
|
||||
|
||||
use crate::cli::{process_cli_args, Cli};
|
||||
use crate::fs::walk_builder;
|
||||
@ -49,7 +50,13 @@ pub fn main() -> anyhow::Result<()> {
|
||||
})
|
||||
});
|
||||
|
||||
let mut printer = Printer::new(cli_args.print_mode);
|
||||
let printer_config = PrinterConfig {
|
||||
mode: cli_args.print_mode,
|
||||
absolute_paths: cli_args.absolute_paths,
|
||||
colored_output: cli_args.colored_output,
|
||||
..Default::default()
|
||||
};
|
||||
let mut printer = Printer::new(printer_config);
|
||||
queue
|
||||
.into_iter()
|
||||
.for_each(|file_results| printer.write(file_results).unwrap());
|
||||
|
124
src/printer.rs
124
src/printer.rs
@ -1,8 +1,13 @@
|
||||
use std::io::{self, Result, StdoutLock, Write};
|
||||
use std::{
|
||||
env::current_dir,
|
||||
io::{Result, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||
|
||||
use crate::search::FileResults;
|
||||
use crate::search::{FileResults, SearchResult};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum PrintMode {
|
||||
Text,
|
||||
Json,
|
||||
@ -10,33 +15,120 @@ pub enum PrintMode {
|
||||
}
|
||||
|
||||
pub struct Printer {
|
||||
writer: io::BufWriter<StdoutLock<'static>>,
|
||||
mode: PrintMode,
|
||||
writer: BufferWriter,
|
||||
buffer: Buffer,
|
||||
config: PrinterConfig,
|
||||
cwd: PathBuf,
|
||||
}
|
||||
|
||||
pub struct PrinterConfig {
|
||||
pub mode: PrintMode,
|
||||
// TODO: refactorize this
|
||||
pub colored_output: bool,
|
||||
pub color_specs: ColorSpecs,
|
||||
pub absolute_paths: bool,
|
||||
}
|
||||
|
||||
impl Default for PrinterConfig {
|
||||
fn default() -> PrinterConfig {
|
||||
PrinterConfig {
|
||||
mode: PrintMode::Text,
|
||||
colored_output: true,
|
||||
color_specs: ColorSpecs::default(),
|
||||
absolute_paths: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ColorSpecs {
|
||||
paths: ColorSpec,
|
||||
line_numbers: ColorSpec,
|
||||
lines: ColorSpec,
|
||||
}
|
||||
|
||||
impl Default for ColorSpecs {
|
||||
fn default() -> ColorSpecs {
|
||||
let mut paths: ColorSpec = ColorSpec::new();
|
||||
paths
|
||||
.set_fg(Some(Color::Green))
|
||||
.set_italic(true)
|
||||
.set_bold(true)
|
||||
.set_underline(true);
|
||||
let mut line_numbers: ColorSpec = ColorSpec::new();
|
||||
line_numbers.set_fg(Some(Color::Yellow)).set_bold(true);
|
||||
let mut lines: ColorSpec = ColorSpec::new();
|
||||
lines.set_fg(Some(Color::White));
|
||||
ColorSpecs {
|
||||
paths,
|
||||
line_numbers,
|
||||
lines,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
pub fn new(mode: PrintMode) -> Printer {
|
||||
let stdout = io::stdout();
|
||||
let lock = stdout.lock();
|
||||
pub fn new(config: PrinterConfig) -> Printer {
|
||||
let color_choice = if config.mode == PrintMode::Text {
|
||||
ColorChoice::Always
|
||||
} else {
|
||||
ColorChoice::Never
|
||||
};
|
||||
let bufwriter = BufferWriter::stdout(color_choice);
|
||||
let buffer = bufwriter.buffer();
|
||||
Printer {
|
||||
writer: io::BufWriter::new(lock),
|
||||
mode,
|
||||
writer: bufwriter,
|
||||
buffer,
|
||||
config,
|
||||
cwd: current_dir().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, results: FileResults) -> Result<()> {
|
||||
self.writeln_to_buffer(match self.mode {
|
||||
PrintMode::Text => format!("{}", results),
|
||||
PrintMode::Json => serde_json::to_string(&results)?,
|
||||
PrintMode::Files => format!("{}", results.path.to_string_lossy()),
|
||||
let path: &Path;
|
||||
if self.config.absolute_paths {
|
||||
path = &results.path;
|
||||
} else {
|
||||
path = results.path.strip_prefix(self.cwd.clone()).unwrap();
|
||||
}
|
||||
match self.config.mode {
|
||||
PrintMode::Text => self.write_colored(path, results.results),
|
||||
PrintMode::Json => self.writeln_to_buffer(serde_json::to_string(&results)?),
|
||||
PrintMode::Files => {
|
||||
self.writeln_to_buffer(format!("{}", results.path.to_string_lossy()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_colored(&mut self, path: &Path, search_results: Vec<SearchResult>) -> Result<()> {
|
||||
self.write_colored_path(path)?;
|
||||
self.write_colored_search_results(search_results)?;
|
||||
self.write_newline_to_buffer()
|
||||
}
|
||||
|
||||
fn write_colored_path(&mut self, path: &Path) -> Result<()> {
|
||||
self.buffer.set_color(&self.config.color_specs.paths)?;
|
||||
writeln!(&mut self.buffer, "{}", path.to_string_lossy())
|
||||
}
|
||||
|
||||
fn write_colored_search_results(&mut self, results: Vec<SearchResult>) -> Result<()> {
|
||||
results.iter().try_for_each(|result| {
|
||||
self.buffer
|
||||
.set_color(&self.config.color_specs.line_numbers)?;
|
||||
write!(&mut self.buffer, "{}:\t", result.line_number)?;
|
||||
self.buffer.set_color(&self.config.color_specs.lines)?;
|
||||
write!(&mut self.buffer, "{}", result.line)
|
||||
})
|
||||
}
|
||||
|
||||
fn writeln_to_buffer(&mut self, text: String) -> Result<()> {
|
||||
writeln!(self.writer, "{}", text)
|
||||
writeln!(self.buffer, "{}", text)
|
||||
}
|
||||
|
||||
fn write_newline_to_buffer(&mut self) -> Result<()> {
|
||||
writeln!(self.buffer, "")
|
||||
}
|
||||
|
||||
pub fn print(&mut self) -> Result<()> {
|
||||
self.writer.flush()
|
||||
self.writer.print(&self.buffer)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user