summaryrefslogtreecommitdiffstats
path: root/src/handlers
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2021-12-05 17:47:30 -0500
committerDan Davison <dandavison7@gmail.com>2021-12-05 19:27:40 -0500
commit3ec93061822301024041b383db571cb9d6c9a49d (patch)
treeca50f468546985969cd28e8816ef2c7a24816b11 /src/handlers
parente7456d4e2884afa420b329d61a0c9b8dfc3f915a (diff)
Gracefully handle failure to parse hunk header
Fixes #765
Diffstat (limited to 'src/handlers')
-rw-r--r--src/handlers/hunk.rs12
-rw-r--r--src/handlers/hunk_header.rs165
-rw-r--r--src/handlers/merge_conflict.rs2
-rw-r--r--src/handlers/submodule.rs4
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()?;