From bffa1efded7359b57d862a25422ef6b386ff9e4c Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Mon, 23 Aug 2021 00:24:49 -0700 Subject: More minimalistic display of submodule (short) diffs (#700) * Rename state machine state: Submodule -> SubmoduleLog * More minimalistic display of submodule log diffs Partially fixes #662 * Defer emitting hunk header until first true hunk line A consequence is that we do not emit the (pseudo) hunk header in submodule (short) state; see #662. --- src/config.rs | 4 +-- src/delta.rs | 60 ++++++++++++++++++++++++++++++----------- src/hunk_header.rs | 6 ++--- src/paint.rs | 2 +- src/parse.rs | 12 +++++++++ src/tests/test_example_diffs.rs | 2 +- 6 files changed, 64 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/config.rs b/src/config.rs index acf7d0c9..9b1c23ce 100644 --- a/src/config.rs +++ b/src/config.rs @@ -99,8 +99,8 @@ impl Config { State::HunkPlus(_) => &self.plus_style, State::CommitMeta => &self.commit_style, State::FileMeta => &self.file_style, - State::HunkHeader => &self.hunk_header_style, - State::Submodule => &self.file_style, + State::HunkHeader(_, _) => &self.hunk_header_style, + State::SubmoduleLog => &self.file_style, _ => delta_unreachable("Unreachable code reached in get_style."), } } diff --git a/src/delta.rs b/src/delta.rs index c42ba9f1..fca76ff8 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -18,13 +18,14 @@ use crate::style::{self, DecorationStyle}; #[derive(Clone, Debug, PartialEq)] pub enum State { - CommitMeta, // In commit metadata section + CommitMeta, // In commit metadata section FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk - HunkHeader, // In hunk metadata line + HunkHeader(String, String), // In hunk metadata line (line, raw_line) HunkZero, // In hunk; unchanged line HunkMinus(Option), // In hunk; removed line (raw_line) HunkPlus(Option), // In hunk; added line (raw_line) - Submodule, + SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log + SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short Unknown, } @@ -39,7 +40,7 @@ impl State { fn is_in_hunk(&self) -> bool { matches!( *self, - State::HunkHeader | State::HunkZero | State::HunkMinus(_) | State::HunkPlus(_) + State::HunkHeader(_, _) | State::HunkZero | State::HunkMinus(_) | State::HunkPlus(_) ) } } @@ -145,7 +146,13 @@ impl<'a> StateMachine<'a> { { self.handle_additional_cases(State::FileMeta)? } else if line.starts_with("Submodule ") { - self.handle_additional_cases(State::Submodule)? + self.handle_additional_cases(State::SubmoduleLog)? + } else if (matches!(self.state, State::HunkHeader(_, _)) + && line.starts_with("-Subproject commit ")) + || (matches!(self.state, State::SubmoduleShort(_)) + && line.starts_with("+Subproject commit ")) + { + self.handle_submodule_short_line()? } else if self.state.is_in_hunk() { // A true hunk line should start with one of: '+', '-', ' '. However, handle_hunk_line // handles all lines until the state transitions away from the hunk states. @@ -415,14 +422,39 @@ impl<'a> StateMachine<'a> { Ok(handled_line) } - /// Emit the hunk header, with any requested decoration. + fn handle_submodule_short_line(&mut self) -> std::io::Result { + if let Some(commit) = parse::get_submodule_short_commit(&self.line) { + if let State::HunkHeader(_, _) = self.state { + self.state = State::SubmoduleShort(commit.to_owned()); + } else if let State::SubmoduleShort(minus_commit) = &self.state { + self.painter.emit()?; + writeln!( + self.painter.writer, + "{} ⟶ {}", + self.config + .minus_style + .paint(minus_commit.chars().take(7).collect::()), + self.config + .plus_style + .paint(commit.chars().take(7).collect::()), + )?; + } + } + Ok(true) + } + fn handle_hunk_header_line(&mut self) -> std::io::Result { + self.state = State::HunkHeader(self.line.clone(), self.raw_line.clone()); + Ok(true) + } + + /// Emit the hunk header, with any requested decoration. + fn emit_hunk_header_line(&mut self, line: &str, raw_line: &str) -> std::io::Result { self.painter.paint_buffered_minus_and_plus_lines(); - self.state = State::HunkHeader; self.painter.set_highlighter(); self.painter.emit()?; - let (code_fragment, line_numbers) = parse::parse_hunk_header(&self.line); + let (code_fragment, line_numbers) = parse::parse_hunk_header(line); if self.config.line_numbers { self.painter .line_numbers_data @@ -430,12 +462,7 @@ impl<'a> StateMachine<'a> { } if self.config.hunk_header_style.is_raw { - hunk_header::write_hunk_header_raw( - &mut self.painter, - &self.line, - &self.raw_line, - self.config, - )?; + hunk_header::write_hunk_header_raw(&mut self.painter, line, raw_line, self.config)?; } else if self.config.hunk_header_style.is_omitted { writeln!(self.painter.writer)?; } else { @@ -449,7 +476,7 @@ impl<'a> StateMachine<'a> { &code_fragment, &line_numbers, &mut self.painter, - &self.line, + line, &self.plus_file, self.config, )?; @@ -473,6 +500,9 @@ impl<'a> StateMachine<'a> { { self.painter.paint_buffered_minus_and_plus_lines(); } + if let State::HunkHeader(line, raw_line) = &self.state.clone() { + self.emit_hunk_header_line(line, raw_line)?; + } self.state = match self.line.chars().next() { Some('-') => { if let State::HunkPlus(_) = self.state { diff --git a/src/hunk_header.rs b/src/hunk_header.rs index c076041c..b3c596cf 100644 --- a/src/hunk_header.rs +++ b/src/hunk_header.rs @@ -142,18 +142,18 @@ fn write_to_output_buffer( if !line.is_empty() { let lines = vec![( painter.expand_tabs(line.graphemes(true)), - delta::State::HunkHeader, + delta::State::HunkHeader("".to_owned(), "".to_owned()), )]; let syntax_style_sections = Painter::get_syntax_style_sections_for_lines( &lines, - &delta::State::HunkHeader, + &delta::State::HunkHeader("".to_owned(), "".to_owned()), painter.highlighter.as_mut(), painter.config, ); Painter::paint_lines( syntax_style_sections, vec![vec![(config.hunk_header_style, &lines[0].0)]], // TODO: compute style from state - [delta::State::HunkHeader].iter(), + [delta::State::HunkHeader("".to_owned(), "".to_owned())].iter(), &mut painter.output_buffer, config, &mut None, diff --git a/src/paint.rs b/src/paint.rs index 88f26bfa..2236ec2d 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -463,7 +463,7 @@ impl<'a> Painter<'a> { config.plus_style.is_syntax_highlighted || config.plus_emph_style.is_syntax_highlighted } - State::HunkHeader => true, + State::HunkHeader(_, _) => true, State::HunkMinus(Some(_)) | State::HunkPlus(Some(_)) => false, _ => panic!( "should_compute_syntax_highlighting is undefined for state {:?}", diff --git a/src/parse.rs b/src/parse.rs index a62d486c..c6a0860f 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -268,6 +268,18 @@ fn get_extension(s: &str) -> Option<&str> { .or_else(|| path.file_name().and_then(|s| s.to_str())) } +lazy_static! { + static ref SUBMODULE_SHORT_LINE_REGEX: Regex = + Regex::new("^[-+]Subproject commit ([0-9a-f]{40})$").unwrap(); +} + +pub fn get_submodule_short_commit(line: &str) -> Option<&str> { + match SUBMODULE_SHORT_LINE_REGEX.captures(line) { + Some(caps) => Some(caps.get(1).unwrap().as_str()), + None => None, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/tests/test_example_diffs.rs b/src/tests/test_example_diffs.rs index 3b249eff..d60067b6 100644 --- a/src/tests/test_example_diffs.rs +++ b/src/tests/test_example_diffs.rs @@ -1386,7 +1386,7 @@ src/align.rs:71: impl<'a> Alignment<'a> { │ 4, "impl<'a> Alignment<'a> { ", "rs", - State::HunkHeader, + State::HunkHeader("".to_owned(), "".to_owned()), &config, ); ansi_test_utils::assert_line_has_no_color(&output, 12, "─────────────────────────────┘"); -- cgit v1.2.3