Skip to content

Commit

Permalink
Add side-by-side line wrapping mode
Browse files Browse the repository at this point in the history
If the current line does not fit into the panel, then
it is not truncated but split into multiple lines. A
wrapping symbol is placed at the end of the line.

Wrapping is limited to a certain number of lines, if this
is exceeded the line is truncated by a now highlighted
truncation symbol.

Commandline argument `-S` / `--side-by-side-wrapped`.

Also adapted `--keep-plus-minus-markers` logic, required
to calculate the exact remaining panel width.
  • Loading branch information
th1000s committed Feb 2, 2021
1 parent 10b3914 commit 18d1b5b
Show file tree
Hide file tree
Showing 14 changed files with 973 additions and 25 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ itertools = "0.10.0"
lazy_static = "1.4"
regex = "1.4.3"
shell-words = "1.0.0"
static_assertions = "1.1.0"
structopt = "0.3.21"
unicode-segmentation = "1.7.1"
unicode-width = "0.1.8"
Expand Down
1 change: 1 addition & 0 deletions src/ansi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use iterator::{AnsiElementIterator, Element};
pub const ANSI_CSI_CLEAR_TO_EOL: &str = "\x1b[0K";
pub const ANSI_CSI_CLEAR_TO_BOL: &str = "\x1b[1K";
pub const ANSI_SGR_RESET: &str = "\x1b[0m";
pub const ANSI_SGR_REVERSE: &str = "\x1b[7m";

pub fn strip_ansi_codes(s: &str) -> String {
strip_ansi_codes_from_strings_iterator(ansi_strings_iterator(s))
Expand Down
4 changes: 4 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ pub struct Opt {
#[structopt(short = "s", long = "side-by-side")]
pub side_by_side: bool,

/// Display a side-by-side diff and wrap overlong lines instead of truncating them.
#[structopt(short = "S", long = "side-by-side-wrapped")]
pub side_by_side_wrapped: bool,

#[structopt(long = "diff-highlight")]
/// Emulate diff-highlight (https://github.com/git/git/tree/master/contrib/diff-highlight)
pub diff_highlight: bool,
Expand Down
16 changes: 15 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use syntect::highlighting::Style as SyntectStyle;
use syntect::highlighting::Theme as SyntaxTheme;
use syntect::parsing::SyntaxSet;

use crate::ansi;
use crate::bat_utils::output::PagingMode;
use crate::cli;
use crate::color;
Expand Down Expand Up @@ -69,6 +70,7 @@ pub struct Config {
pub git_minus_style: Style,
pub git_plus_style: Style,
pub side_by_side: bool,
pub side_by_side_wrapped: bool,
pub side_by_side_data: side_by_side::SideBySideData,
pub syntax_dummy_theme: SyntaxTheme,
pub syntax_set: SyntaxSet,
Expand All @@ -78,6 +80,8 @@ pub struct Config {
pub true_color: bool,
pub truncation_symbol: String,
pub whitespace_error_style: Style,
pub wrap_symbol: String,
pub wrap_max_lines: usize,
pub zero_style: Style,
}

Expand Down Expand Up @@ -214,14 +218,24 @@ impl From<cli::Opt> for Config {
git_minus_style,
git_plus_style,
side_by_side: opt.side_by_side,
side_by_side_wrapped: opt.side_by_side_wrapped,
side_by_side_data,
syntax_dummy_theme: SyntaxTheme::default(),
syntax_set: opt.computed.syntax_set,
syntax_theme: opt.computed.syntax_theme,
tab_width: opt.tab_width,
tokenization_regex,
true_color: opt.computed.true_color,
truncation_symbol: "→".to_string(),
truncation_symbol: {
let sym = "→";
if opt.side_by_side_wrapped {
format!("{}{}{}", ansi::ANSI_SGR_REVERSE, sym, ansi::ANSI_SGR_RESET)
} else {
sym.to_string()
}
},
wrap_symbol: "↵".to_string(),
wrap_max_lines: 5,
whitespace_error_style,
zero_style,
}
Expand Down
4 changes: 4 additions & 0 deletions src/delta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub enum State {
HunkMinus(Option<String>), // In hunk; removed line (raw_line)
HunkPlus(Option<String>), // In hunk; added line (raw_line)
Unknown,
// The following elements are created when a line is wrapped to display it:
HunkZeroWrapped, // Wrapped unchanged line in side-by-side mode
HunkMinusWrapped, // Wrapped removed line in side-by-side mode
HunkPlusWrapped, // Wrapped added line in side-by-side mode
}

#[derive(Debug, PartialEq)]
Expand Down
5 changes: 4 additions & 1 deletion src/features/line_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,20 @@ pub fn format_and_paint_line_numbers<'a>(
*m_ref += 1;
((Some(m), None), (minus_style, plus_style))
}
State::HunkMinusWrapped => ((None, None), (minus_style, plus_style)),
State::HunkZero => {
let (m, p) = (*m_ref, *p_ref);
*m_ref += 1;
*p_ref += 1;
((Some(m), Some(p)), (zero_style, zero_style))
}
State::HunkZeroWrapped => ((None, None), (zero_style, zero_style)),
State::HunkPlus(_) => {
let p = *p_ref;
*p_ref += 1;
((None, Some(p)), (minus_style, plus_style))
}
State::HunkPlusWrapped => ((None, None), (minus_style, plus_style)),
_ => return Vec::new(),
};

Expand Down Expand Up @@ -153,7 +156,7 @@ lazy_static! {
.unwrap();
}

#[derive(Default)]
#[derive(Default, Debug)]
pub struct LineNumbersData<'a> {
pub left_format_data: LineNumberFormatData<'a>,
pub right_format_data: LineNumberFormatData<'a>,
Expand Down
5 changes: 5 additions & 0 deletions src/features/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ pub fn make_builtin_features() -> HashMap<String, BuiltinFeature> {
"side-by-side".to_string(),
side_by_side::make_feature().into_iter().collect(),
),
(
"side-by-side-wrapped".to_string(),
wrap::make_feature().into_iter().collect(),
),
]
.into_iter()
.collect()
Expand Down Expand Up @@ -90,6 +94,7 @@ pub mod line_numbers;
pub mod navigate;
pub mod raw;
pub mod side_by_side;
pub mod wrap;

#[cfg(test)]
pub mod tests {
Expand Down
44 changes: 31 additions & 13 deletions src/features/side_by_side.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::cli;
use crate::config::Config;
use crate::delta::State;
use crate::features::line_numbers;
use crate::features::wrap;
use crate::features::OptionValueFunction;
use crate::paint::Painter;
use crate::style::Style;
Expand Down Expand Up @@ -35,6 +36,7 @@ pub struct SideBySideData {
pub right_panel: Panel,
}

#[derive(Debug)]
pub struct Panel {
pub width: usize,
pub offset: usize,
Expand All @@ -61,13 +63,13 @@ impl SideBySideData {

/// Emit a sequence of minus and plus lines in side-by-side mode.
#[allow(clippy::too_many_arguments)]
pub fn paint_minus_and_plus_lines_side_by_side<'a>(
pub fn paint_minus_and_plus_lines_side_by_side(
minus_syntax_style_sections: Vec<Vec<(SyntectStyle, &str)>>,
minus_diff_style_sections: Vec<Vec<(Style, &str)>>,
minus_states: Vec<&'a State>,
minus_states: Vec<State>,
plus_syntax_style_sections: Vec<Vec<(SyntectStyle, &str)>>,
plus_diff_style_sections: Vec<Vec<(Style, &str)>>,
plus_states: Vec<&'a State>,
plus_states: Vec<State>,
line_alignment: Vec<(Option<usize>, Option<usize>)>,
output_buffer: &mut String,
config: &Config,
Expand All @@ -80,7 +82,7 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>(
&minus_syntax_style_sections,
&minus_diff_style_sections,
match minus_line_index {
Some(i) => minus_states[i],
Some(i) => &minus_states[i],
None => &State::HunkMinus(None),
},
line_numbers_data,
Expand All @@ -92,7 +94,7 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>(
&plus_syntax_style_sections,
&plus_diff_style_sections,
match plus_line_index {
Some(i) => plus_states[i],
Some(i) => &plus_states[i],
None => &State::HunkPlus(None),
},
line_numbers_data,
Expand All @@ -113,16 +115,29 @@ pub fn paint_zero_lines_side_by_side(
painted_prefix: Option<ansi_term::ANSIString>,
background_color_extends_to_terminal_width: Option<bool>,
) {
let state = State::HunkZero;
let states = vec![State::HunkZero];

for (line_index, (syntax_sections, diff_sections)) in syntax_style_sections
.iter()
let (states, syntax_style_sections, diff_style_sections) = if config.side_by_side_wrapped {
wrap::wrap_zero_block(
&config,
states,
syntax_style_sections,
diff_style_sections,
&line_numbers_data,
)
} else {
(states, syntax_style_sections, diff_style_sections)
};

for (line_index, ((syntax_sections, diff_sections), state)) in syntax_style_sections
.into_iter()
.zip_eq(diff_style_sections.iter())
.zip_eq(states.into_iter())
.enumerate()
{
for panel_side in &[PanelSide::Left, PanelSide::Right] {
let (mut panel_line, panel_line_is_empty) = Painter::paint_line(
syntax_sections,
&syntax_sections,
diff_sections,
&state,
line_numbers_data,
Expand All @@ -142,7 +157,7 @@ pub fn paint_zero_lines_side_by_side(
);
output_buffer.push_str(&panel_line);

if panel_side == &PanelSide::Left {
if panel_side == &PanelSide::Left && state != State::HunkZeroWrapped {
// TODO: Avoid doing the superimpose_style_sections work twice.
// HACK: These are getting incremented twice, so knock them back down once.
if let Some(d) = line_numbers_data.as_mut() {
Expand Down Expand Up @@ -303,9 +318,12 @@ fn paint_minus_or_plus_panel_line(
)
};

let painted_prefix = match (config.keep_plus_minus_markers, panel_side) {
(true, PanelSide::Left) => Some(config.minus_style.paint("-")),
(true, PanelSide::Right) => Some(config.plus_style.paint("+")),
let painted_prefix = match (config.keep_plus_minus_markers, panel_side, state) {
(true, _, State::HunkPlusWrapped) | (true, _, State::HunkMinusWrapped) => {
Some(config.plus_style.paint(" "))
}
(true, PanelSide::Left, _) => Some(config.minus_style.paint("-")),
(true, PanelSide::Right, _) => Some(config.plus_style.paint("+")),
_ => None,
};

Expand Down

0 comments on commit 18d1b5b

Please sign in to comment.