mirror of
https://github.com/sharkdp/bat.git
synced 2025-01-22 21:48:56 +01:00
Implementation of 'bat --diff'
This adds a new `--diff` option that can be used to only show lines close to Git changes (added/removed/modified lines). The amount of additional context can be controlled with `--diff-context=N`. closes #23
This commit is contained in:
parent
0064321323
commit
82e7786e74
@ -2,6 +2,11 @@
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Add a new `--diff` option that can be used to only show lines surrounding
|
||||
Git changes, i.e. added, removed or modified lines. The amount of additional
|
||||
context can be controlled with `--diff-context=N`. See #23 and #940
|
||||
|
||||
## Bugfixes
|
||||
## Other
|
||||
## `bat` as a library
|
||||
|
@ -15,7 +15,7 @@ use console::Term;
|
||||
|
||||
use bat::{
|
||||
assets::HighlightingAssets,
|
||||
config::Config,
|
||||
config::{Config, VisibleLines},
|
||||
error::*,
|
||||
input::Input,
|
||||
line_range::{HighlightedLineRanges, LineRange, LineRanges},
|
||||
@ -196,13 +196,23 @@ impl App {
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| String::from(HighlightingAssets::default_theme())),
|
||||
line_ranges: self
|
||||
.matches
|
||||
.values_of("line-range")
|
||||
.map(|vs| vs.map(LineRange::from).collect())
|
||||
.transpose()?
|
||||
.map(LineRanges::from)
|
||||
.unwrap_or_default(),
|
||||
visible_lines: if self.matches.is_present("diff") {
|
||||
VisibleLines::DiffContext(
|
||||
self.matches
|
||||
.value_of("diff-context")
|
||||
.and_then(|t| t.parse().ok())
|
||||
.unwrap_or(2),
|
||||
)
|
||||
} else {
|
||||
VisibleLines::Ranges(
|
||||
self.matches
|
||||
.values_of("line-range")
|
||||
.map(|vs| vs.map(LineRange::from).collect())
|
||||
.transpose()?
|
||||
.map(LineRanges::from)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
},
|
||||
style_components,
|
||||
syntax_mapping,
|
||||
pager: self.matches.value_of("pager"),
|
||||
|
@ -105,6 +105,34 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
data to bat from STDIN when bat does not otherwise know \
|
||||
the filename."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("diff")
|
||||
.long("diff")
|
||||
.help("Only show lines that have been added/removed/modified.")
|
||||
.long_help(
|
||||
"Only show lines that have been added/removed/modified with respect \
|
||||
to the Git index. Use --diff-context=N to control how much context you want to see.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("diff-context")
|
||||
.long("diff-context")
|
||||
.overrides_with("diff-context")
|
||||
.takes_value(true)
|
||||
.value_name("N")
|
||||
.validator(
|
||||
|n| {
|
||||
n.parse::<usize>()
|
||||
.map_err(|_| "must be a number")
|
||||
.map(|_| ()) // Convert to Result<(), &str>
|
||||
.map_err(|e| e.to_string())
|
||||
}, // Convert to Result<(), String>
|
||||
)
|
||||
.hidden_short_help(true)
|
||||
.long_help(
|
||||
"Include N lines of context around added/removed/modified lines when using '--diff'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tabs")
|
||||
.long("tabs")
|
||||
@ -339,6 +367,7 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.value_name("N:M")
|
||||
.conflicts_with("diff")
|
||||
.help("Only print the lines from N to M.")
|
||||
.long_help(
|
||||
"Only print the specified range of lines for each file. \
|
||||
|
@ -5,6 +5,32 @@ use crate::style::StyleComponents;
|
||||
use crate::syntax_mapping::SyntaxMapping;
|
||||
use crate::wrapping::WrappingMode;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VisibleLines {
|
||||
/// Show all lines which are included in the line ranges
|
||||
Ranges(LineRanges),
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
/// Only show lines surrounding added/deleted/modified lines
|
||||
DiffContext(usize),
|
||||
}
|
||||
|
||||
impl VisibleLines {
|
||||
pub fn diff_context(&self) -> bool {
|
||||
match self {
|
||||
Self::Ranges(_) => false,
|
||||
#[cfg(feature = "git")]
|
||||
Self::DiffContext(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VisibleLines {
|
||||
fn default() -> Self {
|
||||
VisibleLines::Ranges(LineRanges::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Config<'a> {
|
||||
/// The explicitly configured language, if any
|
||||
@ -39,8 +65,8 @@ pub struct Config<'a> {
|
||||
#[cfg(feature = "paging")]
|
||||
pub paging_mode: PagingMode,
|
||||
|
||||
/// Specifies the lines that should be printed
|
||||
pub line_ranges: LineRanges,
|
||||
/// Specifies which lines should be printed
|
||||
pub visible_lines: VisibleLines,
|
||||
|
||||
/// The syntax highlighting theme
|
||||
pub theme: String,
|
||||
@ -62,10 +88,7 @@ pub struct Config<'a> {
|
||||
fn default_config_should_include_all_lines() {
|
||||
use crate::line_range::RangeCheckResult;
|
||||
|
||||
assert_eq!(
|
||||
Config::default().line_ranges.check(17),
|
||||
RangeCheckResult::InRange
|
||||
);
|
||||
assert_eq!(LineRanges::default().check(17), RangeCheckResult::InRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,9 +1,13 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::config::Config;
|
||||
use crate::config::{Config, VisibleLines};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::diff::{get_git_diff, LineChanges};
|
||||
use crate::error::*;
|
||||
use crate::input::{Input, InputReader, OpenedInput};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::line_range::LineRange;
|
||||
use crate::line_range::{LineRanges, RangeCheckResult};
|
||||
use crate::output::OutputType;
|
||||
#[cfg(feature = "paging")]
|
||||
@ -68,6 +72,32 @@ impl<'b> Controller<'b> {
|
||||
no_errors = false;
|
||||
}
|
||||
Ok(mut opened_input) => {
|
||||
#[cfg(feature = "git")]
|
||||
let line_changes = if self.config.visible_lines.diff_context()
|
||||
|| (!self.config.loop_through && self.config.style_components.changes())
|
||||
{
|
||||
if let crate::input::OpenedInputKind::OrdinaryFile(ref path) =
|
||||
opened_input.kind
|
||||
{
|
||||
let diff = get_git_diff(path);
|
||||
|
||||
if self.config.visible_lines.diff_context()
|
||||
&& diff
|
||||
.as_ref()
|
||||
.map(|changes| changes.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
diff
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut printer: Box<dyn Printer> = if self.config.loop_through {
|
||||
Box::new(SimplePrinter::new())
|
||||
} else {
|
||||
@ -75,10 +105,18 @@ impl<'b> Controller<'b> {
|
||||
&self.config,
|
||||
&self.assets,
|
||||
&mut opened_input,
|
||||
#[cfg(feature = "git")]
|
||||
&line_changes,
|
||||
))
|
||||
};
|
||||
|
||||
let result = self.print_file(&mut *printer, writer, &mut opened_input);
|
||||
let result = self.print_file(
|
||||
&mut *printer,
|
||||
writer,
|
||||
&mut opened_input,
|
||||
#[cfg(feature = "git")]
|
||||
&line_changes,
|
||||
);
|
||||
|
||||
if let Err(error) = result {
|
||||
handle_error(&error);
|
||||
@ -96,13 +134,31 @@ impl<'b> Controller<'b> {
|
||||
printer: &mut dyn Printer,
|
||||
writer: &mut dyn Write,
|
||||
input: &mut OpenedInput,
|
||||
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
|
||||
) -> Result<()> {
|
||||
if !input.reader.first_line.is_empty() || self.config.style_components.header() {
|
||||
printer.print_header(writer, input)?;
|
||||
}
|
||||
|
||||
if !input.reader.first_line.is_empty() {
|
||||
self.print_file_ranges(printer, writer, &mut input.reader, &self.config.line_ranges)?;
|
||||
let line_ranges = match self.config.visible_lines {
|
||||
VisibleLines::Ranges(ref line_ranges) => line_ranges.clone(),
|
||||
#[cfg(feature = "git")]
|
||||
VisibleLines::DiffContext(context) => {
|
||||
let mut line_ranges: Vec<LineRange> = vec![];
|
||||
|
||||
if let Some(line_changes) = line_changes {
|
||||
for line in line_changes.keys() {
|
||||
let line = *line as usize;
|
||||
line_ranges.push(LineRange::new(line - context, line + context));
|
||||
}
|
||||
}
|
||||
|
||||
LineRanges::from(line_ranges)
|
||||
}
|
||||
};
|
||||
|
||||
self.print_file_ranges(printer, writer, &mut input.reader, &line_ranges)?;
|
||||
}
|
||||
printer.print_footer(writer, input)?;
|
||||
|
||||
|
@ -6,7 +6,7 @@ use syntect::parsing::SyntaxReference;
|
||||
|
||||
use crate::{
|
||||
assets::HighlightingAssets,
|
||||
config::Config,
|
||||
config::{Config, VisibleLines},
|
||||
controller::Controller,
|
||||
error::Result,
|
||||
input::Input,
|
||||
@ -205,7 +205,7 @@ impl<'a> PrettyPrinter<'a> {
|
||||
|
||||
/// Specify the lines that should be printed (default: all)
|
||||
pub fn line_ranges(&mut self, ranges: LineRanges) -> &mut Self {
|
||||
self.config.line_ranges = ranges;
|
||||
self.config.visible_lines = VisibleLines::Ranges(ranges);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ use crate::config::Config;
|
||||
use crate::decorations::LineChangesDecoration;
|
||||
use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration};
|
||||
#[cfg(feature = "git")]
|
||||
use crate::diff::{get_git_diff, LineChanges};
|
||||
use crate::diff::LineChanges;
|
||||
use crate::error::*;
|
||||
use crate::input::OpenedInput;
|
||||
use crate::line_range::RangeCheckResult;
|
||||
@ -90,7 +90,7 @@ pub(crate) struct InteractivePrinter<'a> {
|
||||
ansi_prefix_sgr: String,
|
||||
content_type: Option<ContentType>,
|
||||
#[cfg(feature = "git")]
|
||||
pub line_changes: Option<LineChanges>,
|
||||
pub line_changes: &'a Option<LineChanges>,
|
||||
highlighter: Option<HighlightLines<'a>>,
|
||||
syntax_set: &'a SyntaxSet,
|
||||
background_color_highlight: Option<Color>,
|
||||
@ -101,6 +101,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
config: &'a Config,
|
||||
assets: &'a HighlightingAssets,
|
||||
input: &mut OpenedInput,
|
||||
#[cfg(feature = "git")] line_changes: &'a Option<LineChanges>,
|
||||
) -> Self {
|
||||
let theme = assets.get_theme(&config.theme);
|
||||
|
||||
@ -145,9 +146,6 @@ impl<'a> InteractivePrinter<'a> {
|
||||
panel_width = 0;
|
||||
}
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
let mut line_changes = None;
|
||||
|
||||
let highlighter = if input
|
||||
.reader
|
||||
.content_type
|
||||
@ -155,18 +153,6 @@ impl<'a> InteractivePrinter<'a> {
|
||||
{
|
||||
None
|
||||
} else {
|
||||
// Get the Git modifications
|
||||
#[cfg(feature = "git")]
|
||||
{
|
||||
use crate::input::OpenedInputKind;
|
||||
|
||||
if config.style_components.changes() {
|
||||
if let OpenedInputKind::OrdinaryFile(ref path) = input.kind {
|
||||
line_changes = get_git_diff(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the type of syntax for highlighting
|
||||
let syntax = assets.get_syntax(config.language, input, &config.syntax_mapping);
|
||||
Some(HighlightLines::new(syntax, theme))
|
||||
|
Loading…
Reference in New Issue
Block a user