diff options
author | Dan Davison <dandavison7@gmail.com> | 2021-12-05 17:47:30 -0500 |
---|---|---|
committer | Dan Davison <dandavison7@gmail.com> | 2021-12-05 19:27:40 -0500 |
commit | 3ec93061822301024041b383db571cb9d6c9a49d (patch) | |
tree | ca50f468546985969cd28e8816ef2c7a24816b11 /src/handlers | |
parent | e7456d4e2884afa420b329d61a0c9b8dfc3f915a (diff) |
Gracefully handle failure to parse hunk header
Fixes #765
Diffstat (limited to 'src/handlers')
-rw-r--r-- | src/handlers/hunk.rs | 12 | ||||
-rw-r--r-- | src/handlers/hunk_header.rs | 165 | ||||
-rw-r--r-- | src/handlers/merge_conflict.rs | 2 | ||||
-rw-r--r-- | src/handlers/submodule.rs | 4 |
4 files changed, 112 insertions, 71 deletions
diff --git a/src/handlers/hunk.rs b/src/handlers/hunk.rs index a0bec617..e580dfa6 100644 --- a/src/handlers/hunk.rs +++ b/src/handlers/hunk.rs @@ -29,7 +29,7 @@ impl<'a> StateMachine<'a> { fn test_hunk_line(&self) -> bool { matches!( self.state, - State::HunkHeader(_, _, _) + State::HunkHeader(_, _, _, _) | State::HunkZero(_) | State::HunkMinus(_, _) | State::HunkPlus(_, _) @@ -59,8 +59,8 @@ 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)?; + if let State::HunkHeader(_, parsed_hunk_header, line, raw_line) = &self.state.clone() { + self.emit_hunk_header_line(parsed_hunk_header, line, raw_line)?; } self.state = match new_line_state(&self.line, &self.state) { Some(HunkMinus(diff_type, _)) => { @@ -140,13 +140,13 @@ fn new_line_state(new_line: &str, prev_state: &State) -> Option<State> { HunkMinus(Unified, _) | HunkZero(Unified) | HunkPlus(Unified, _) - | HunkHeader(Unified, _, _) => Unified, - HunkHeader(Combined(Number(n), InMergeConflict::No), _, _) => { + | HunkHeader(Unified, _, _, _) => Unified, + HunkHeader(Combined(Number(n), InMergeConflict::No), _, _, _) => { Combined(Number(*n), InMergeConflict::No) } // The prefixes are specific to the previous line, but the number of merge parents remains // equal to the prefix length. - HunkHeader(Combined(Prefix(prefix), InMergeConflict::No), _, _) => { + HunkHeader(Combined(Prefix(prefix), InMergeConflict::No), _, _, _) => { Combined(Number(prefix.len()), InMergeConflict::No) } HunkMinus(Combined(Prefix(prefix), in_merge_conflict), _) diff --git a/src/handlers/hunk_header.rs b/src/handlers/hunk_header.rs index 3ecb86c8..84b5bf64 100644 --- a/src/handlers/hunk_header.rs +++ b/src/handlers/hunk_header.rs @@ -30,6 +30,12 @@ use crate::delta::{self, DiffType, InMergeConflict, MergeParents, State, StateMa use crate::paint::{self, BgShouldFill, Painter, StyleSectionSpecifier}; use crate::style::DecorationStyle; +#[derive(Clone, Default, Debug, PartialEq)] +pub struct ParsedHunkHeader { + code_fragment: String, + line_numbers_and_hunk_lengths: Vec<(usize, usize)>, +} + impl<'a> StateMachine<'a> { #[inline] fn test_hunk_header_line(&self) -> bool { @@ -45,39 +51,57 @@ impl<'a> StateMachine<'a> { if !self.test_hunk_header_line() { return Ok(false); } - let diff_type = match &self.state { - DiffHeader(Combined(MergeParents::Unknown, InMergeConflict::No)) => { - // https://git-scm.com/docs/git-diff#_combined_diff_format - let n_parents = self.line.chars().take_while(|c| c == &'@').count() - 1; - Combined(MergeParents::Number(n_parents), InMergeConflict::No) - } - DiffHeader(diff_type) - | HunkMinus(diff_type, _) - | HunkZero(diff_type) - | HunkPlus(diff_type, _) => diff_type.clone(), - Unknown => Unified, - _ => delta_unreachable(&format!( - "Unexpected state in handle_hunk_header: {:?}", - self.state - )), - }; - self.state = HunkHeader(diff_type, self.line.clone(), self.raw_line.clone()); - Ok(true) + let mut handled_line = false; + if let Some(parsed_hunk_header) = parse_hunk_header(&self.line) { + let diff_type = match &self.state { + DiffHeader(Combined(MergeParents::Unknown, InMergeConflict::No)) => { + // https://git-scm.com/docs/git-diff#_combined_diff_format + let n_parents = self.line.chars().take_while(|c| c == &'@').count() - 1; + Combined(MergeParents::Number(n_parents), InMergeConflict::No) + } + DiffHeader(diff_type) + | HunkMinus(diff_type, _) + | HunkZero(diff_type) + | HunkPlus(diff_type, _) => diff_type.clone(), + Unknown => Unified, + _ => delta_unreachable(&format!( + "Unexpected state in handle_hunk_header: {:?}", + self.state + )), + }; + self.state = HunkHeader( + diff_type, + parsed_hunk_header, + self.line.clone(), + self.raw_line.clone(), + ); + handled_line = true; + } + Ok(handled_line) } /// Emit the hunk header, with any requested decoration. - pub fn emit_hunk_header_line(&mut self, line: &str, raw_line: &str) -> std::io::Result<bool> { + pub fn emit_hunk_header_line( + &mut self, + parsed_hunk_header: &ParsedHunkHeader, + line: &str, + raw_line: &str, + ) -> std::io::Result<bool> { self.painter.paint_buffered_minus_and_plus_lines(); self.painter.set_highlighter(); self.painter.emit()?; - let (code_fragment, line_numbers) = parse_hunk_header(line); + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parsed_hunk_header; + if self.config.line_numbers { self.painter .line_numbers_data .as_mut() .unwrap() - .initialize_hunk(&line_numbers, self.plus_file.to_string()); + .initialize_hunk(line_numbers_and_hunk_lengths, self.plus_file.to_string()); } if self.config.hunk_header_style.is_raw { @@ -92,8 +116,8 @@ impl<'a> StateMachine<'a> { } write_hunk_header( - &code_fragment, - &line_numbers, + code_fragment, + line_numbers_and_hunk_lengths, &mut self.painter, line, if self.plus_file == "/dev/null" { @@ -132,25 +156,31 @@ lazy_static! { /// Given input like /// "@@ -74,15 +74,14 @@ pub fn delta(" /// Return " pub fn delta(" and a vector of (line_number, hunk_length) tuples. -fn parse_hunk_header(line: &str) -> (String, Vec<(usize, usize)>) { - let caps = HUNK_HEADER_REGEX.captures(line).unwrap(); - let file_coordinates = &caps[1]; - let line_numbers_and_hunk_lengths = HUNK_HEADER_FILE_COORDINATE_REGEX - .captures_iter(file_coordinates) - .map(|caps| { - ( - caps[1].parse::<usize>().unwrap(), - caps.get(2) - .map(|m| m.as_str()) - // Per the specs linked above, if the hunk length is absent then it is 1. - .unwrap_or("1") - .parse::<usize>() - .unwrap(), - ) +fn parse_hunk_header(line: &str) -> Option<ParsedHunkHeader> { + if let Some(caps) = HUNK_HEADER_REGEX.captures(line) { + let file_coordinates = &caps[1]; + let line_numbers_and_hunk_lengths = HUNK_HEADER_FILE_COORDINATE_REGEX + .captures_iter(file_coordinates) + .map(|caps| { + ( + caps[1].parse::<usize>().unwrap(), + caps.get(2) + .map(|m| m.as_str()) + // Per the specs linked above, if the hunk length is absent then it is 1. + .unwrap_or("1") + .parse::<usize>() + .unwrap(), + ) + }) + .collect(); + let code_fragment = caps[2].to_string(); + Some(ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, }) - .collect(); - let code_fragment = &caps[2]; - (code_fragment.to_string(), line_numbers_and_hunk_lengths) + } else { + None + } } fn write_hunk_header_raw( @@ -177,7 +207,7 @@ fn write_hunk_header_raw( pub fn write_hunk_header( code_fragment: &str, - line_numbers: &[(usize, usize)], + line_numbers_and_hunk_lengths: &[(usize, usize)], painter: &mut Painter, line: &str, plus_file: &str, @@ -193,7 +223,7 @@ pub fn write_hunk_header( "".to_string() }; - let plus_line_number = line_numbers[line_numbers.len() - 1].0; + let plus_line_number = line_numbers_and_hunk_lengths[line_numbers_and_hunk_lengths.len() - 1].0; let file_with_line_number = paint_file_path_with_line_number(Some(plus_line_number), plus_file, config); @@ -272,7 +302,12 @@ fn write_to_output_buffer( painter.syntax_highlight_and_paint_line( &line, StyleSectionSpecifier::Style(config.hunk_header_style), - delta::State::HunkHeader(DiffType::Unified, "".to_owned(), "".to_owned()), + delta::State::HunkHeader( + DiffType::Unified, + ParsedHunkHeader::default(), + "".to_owned(), + "".to_owned(), + ), BgShouldFill::No, ); painter.output_buffer.pop(); // trim newline @@ -287,9 +322,10 @@ pub mod tests { #[test] fn test_parse_hunk_header() { - let parsed = parse_hunk_header("@@ -74,15 +75,14 @@ pub fn delta(\n"); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@ -74,15 +75,14 @@ pub fn delta(\n").unwrap(); assert_eq!(code_fragment, " pub fn delta(\n"); assert_eq!(line_numbers_and_hunk_lengths[0], (74, 15),); assert_eq!(line_numbers_and_hunk_lengths[1], (75, 14),); @@ -297,9 +333,10 @@ pub mod tests { #[test] fn test_parse_hunk_header_with_omitted_hunk_lengths() { - let parsed = parse_hunk_header("@@ -74 +75,2 @@ pub fn delta(\n"); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@ -74 +75,2 @@ pub fn delta(\n").unwrap(); assert_eq!(code_fragment, " pub fn delta(\n"); assert_eq!(line_numbers_and_hunk_lengths[0], (74, 1),); assert_eq!(line_numbers_and_hunk_lengths[1], (75, 2),); @@ -307,9 +344,10 @@ pub mod tests { #[test] fn test_parse_hunk_header_added_file() { - let parsed = parse_hunk_header("@@ -1,22 +0,0 @@"); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@ -1,22 +0,0 @@").unwrap(); assert_eq!(code_fragment, "",); assert_eq!(line_numbers_and_hunk_lengths[0], (1, 22),); assert_eq!(line_numbers_and_hunk_lengths[1], (0, 0),); @@ -317,9 +355,10 @@ pub mod tests { #[test] fn test_parse_hunk_header_deleted_file() { - let parsed = parse_hunk_header("@@ -0,0 +1,3 @@"); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@ -0,0 +1,3 @@").unwrap(); assert_eq!(code_fragment, "",); assert_eq!(line_numbers_and_hunk_lengths[0], (0, 0),); assert_eq!(line_numbers_and_hunk_lengths[1], (1, 3),); @@ -327,9 +366,10 @@ pub mod tests { #[test] fn test_parse_hunk_header_merge() { - let parsed = parse_hunk_header("@@@ -293,11 -358,15 +358,16 @@@ dependencies ="); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@@ -293,11 -358,15 +358,16 @@@ dependencies =").unwrap(); assert_eq!(code_fragment, " dependencies ="); assert_eq!(line_numbers_and_hunk_lengths[0], (293, 11),); assert_eq!(line_numbers_and_hunk_lengths[1], (358, 15),); @@ -338,9 +378,10 @@ pub mod tests { #[test] fn test_parse_hunk_header_cthulhu() { - let parsed = parse_hunk_header("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -444,17 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 +444,17 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ int snd_soc_jack_add_gpios(struct snd_s"); - let code_fragment = parsed.0; - let line_numbers_and_hunk_lengths = parsed.1; + let ParsedHunkHeader { + code_fragment, + line_numbers_and_hunk_lengths, + } = parse_hunk_header("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -444,17 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 -446,6 +444,17 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ int snd_soc_jack_add_gpios(struct snd_s").unwrap(); assert_eq!(code_fragment, " int snd_soc_jack_add_gpios(struct snd_s"); assert_eq!(line_numbers_and_hunk_lengths[0], (446, 6),); assert_eq!(line_numbers_and_hunk_lengths[1], (446, 6),); diff --git a/src/handlers/merge_conflict.rs b/src/handlers/merge_conflict.rs index 03dfb27a..1ff06fc3 100644 --- a/src/handlers/merge_conflict.rs +++ b/src/handlers/merge_conflict.rs @@ -40,7 +40,7 @@ impl<'a> StateMachine<'a> { } match self.state.clone() { - HunkHeader(Combined(merge_parents, InMergeConflict::No), _, _) + HunkHeader(Combined(merge_parents, InMergeConflict::No), _, _, _) | HunkMinus(Combined(merge_parents, InMergeConflict::No), _) | HunkZero(Combined(merge_parents, InMergeConflict::No)) | HunkPlus(Combined(merge_parents, InMergeConflict::No), _) => { diff --git a/src/handlers/submodule.rs b/src/handlers/submodule.rs index 04e273e9..4578594a 100644 --- a/src/handlers/submodule.rs +++ b/src/handlers/submodule.rs @@ -18,7 +18,7 @@ impl<'a> StateMachine<'a> { #[inline] fn test_submodule_short_line(&self) -> bool { - matches!(self.state, State::HunkHeader(_, _, _)) + matches!(self.state, State::HunkHeader(_, _, _, _)) && self.line.starts_with("-Subproject commit ") || matches!(self.state, State::SubmoduleShort(_)) && self.line.starts_with("+Subproject commit ") @@ -29,7 +29,7 @@ impl<'a> StateMachine<'a> { return Ok(false); } if let Some(commit) = get_submodule_short_commit(&self.line) { - if let State::HunkHeader(_, _, _) = self.state { + 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()?; |