From 19276b5b4ec380e1e00d784595efaeaf6ce5f40d Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 2 Oct 2019 13:36:56 -0400 Subject: [PATCH] Replace tabs with spaces Fixes #16 --- Makefile | 1 + src/cli.rs | 7 +++++++ src/config.rs | 2 ++ src/delta.rs | 29 +++++++++++++++++++++++------ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index b96b98ce..fea36c8a 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ test: cargo test bash -c "diff -u <(git log -p | cut -c 2-) \ <(git log -p | delta --width variable \ + --tabs 0 \ --commit-style plain \ --file-style plain \ --hunk-style plain \ diff --git a/src/cli.rs b/src/cli.rs index 5ec01aa9..c8d07329 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -69,6 +69,13 @@ pub struct Opt { #[structopt(short = "w", long = "width")] pub width: Option, + /// The number of spaces to replace tab characters with. Use --tabs=0 to pass tab characters + /// through directly, but note that in that case delta will calculate line widths assuming tabs + /// occupy one character's width on the screen: if your terminal renders tabs as more than than + /// one character wide then delta's output will look incorrect. + #[structopt(long = "tabs", default_value = "4")] + pub tab_width: usize, + /// Show the command-line arguments for the current colors. #[structopt(long = "show-colors")] pub show_colors: bool, diff --git a/src/config.rs b/src/config.rs index e1008676..f855d7c2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ pub struct Config<'a> { pub syntax_set: &'a SyntaxSet, pub terminal_width: usize, pub width: Option, + pub tab_width: usize, pub pager: &'a str, pub opt: &'a cli::Opt, pub no_style: Style, @@ -111,6 +112,7 @@ pub fn get_config<'a>( plus_emph_style_modifier, terminal_width, width, + tab_width: opt.tab_width, syntax_set, pager: "less", opt, diff --git a/src/delta.rs b/src/delta.rs index 8bfaf880..7c99cbe6 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -2,6 +2,8 @@ use std::io::Write; use ansi_term::Colour::{Blue, Yellow}; use console::strip_ansi_codes; +use itertools::Itertools; +use unicode_segmentation::UnicodeSegmentation; use crate::bat::assets::HighlightingAssets; use crate::cli; @@ -222,16 +224,16 @@ fn handle_hunk_line(painter: &mut Painter, line: &str, state: State, config: &Co if state == State::HunkPlus { painter.paint_buffered_lines(); } - painter.minus_lines.push(prepare(&line)); + painter.minus_lines.push(prepare(&line, config.tab_width)); State::HunkMinus } Some('+') => { - painter.plus_lines.push(prepare(&line)); + painter.plus_lines.push(prepare(&line, config.tab_width)); State::HunkPlus } _ => { painter.paint_buffered_lines(); - let line = prepare(&line); + let line = prepare(&line, config.tab_width); let syntax_style_sections = Painter::get_line_syntax_style_sections( &line, &mut painter.highlighter, @@ -251,13 +253,28 @@ fn handle_hunk_line(painter: &mut Painter, line: &str, state: State, config: &Co } } -/// Replace initial -/+ character with ' ', and terminate with newline. +/// Replace initial -/+ character with ' ', expand tabs as spaces, and terminate with newline. // Terminating with newline character is necessary for many of the sublime syntax definitions to // highlight correctly. // See https://docs.rs/syntect/3.2.0/syntect/parsing/struct.SyntaxSetBuilder.html#method.add_from_folder -fn prepare(line: &str) -> String { +fn prepare(line: &str, tab_width: usize) -> String { if !line.is_empty() { - format!(" {}\n", &line[1..]) + let mut line = line.graphemes(true).peekable(); + let mut left_fill = "".to_string(); + + // The first column contains a -/+/space character, added by git. Replace it with a space. + left_fill.push_str(" "); + line.next(); + + // Expand tabs as spaces + if tab_width > 0 { + // tab_width = 0 is documented to mean do not replace tabs. + let n_tabs = line.peeking_take_while(|c| *c == "\t").count(); + if n_tabs > 0 { + left_fill.push_str(&" ".repeat(tab_width * n_tabs)); + } + } + format!("{}{}\n", &left_fill, line.collect::()) } else { "\n".to_string() }