diff --git a/src/parser.rs b/src/parser.rs index 487f3e2..48eb272 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,6 +3,7 @@ use std::error::Error; use chrono::DateTime; use nom::combinator::verify; +use nom::complete::take; use nom::*; use nom::{ branch::alt, @@ -263,32 +264,21 @@ fn is_next_header(input: Input<'_>) -> bool { ///FIXME: Use the ranges in the chunk header to figure out how many chunk lines to parse. Will need /// to figure out how to count in nom more robustly than many1!(). Maybe using switch!()? ///FIXME: The test_parse_triple_plus_minus_hack test will no longer panic when this is fixed. -fn chunk(input: Input) -> IResult { +fn chunk(input: Input) -> IResult { + // First, parse the chunk header to get range information let (input, ranges) = chunk_header(input)?; + let (old_range, new_range, range_hint) = ranges; - // Parse chunk lines, using the range information to guide parsing - let (input, lines) = many0(verify( - alt(( - // Detect added lines - map( - preceded(tuple((char('+'), not(tag("++ ")))), consume_content_line), - Line::Add, - ), - // Detect removed lines - map( - preceded(tuple((char('-'), not(tag("-- ")))), consume_content_line), - Line::Remove, - ), - // Detect context lines - map(preceded(char(' '), consume_content_line), Line::Context), - // Handle empty lines within the chunk - map(tag("\n"), |_| Line::Context("")), - )), - // Stop parsing when we detect the next header or have parsed the expected number of lines - |_| !is_next_header(input), - ))(input)?; + // Calculate total expected lines + let total_context = old_range.count; + let total_added = new_range.count; + + let (input, lines) = parse_hunk_lines( + input, + old_range.count as usize, + new_range.count as usize, + )?; - let (old_range, new_range, range_hint) = ranges; Ok(( input, Hunk { @@ -300,6 +290,86 @@ fn chunk(input: Input) -> IResult { )) } +fn parse_hunk_lines<'a>( + mut input: Input<'a>, + old_count: usize, + new_count: usize, +) -> IResult, Vec>> { + use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::{char, line_ending, not_line_ending}, + combinator::{map, opt}, + sequence::preceded, + IResult, + }; + + enum LineKind<'b> { + Add(&'b str), + Remove(&'b str), + Context(&'b str), + EmptyContext, + } + + let mut lines = Vec::new(); + let mut context = 0; + let mut added = 0; + let mut removed = 0; + + while context + removed < old_count || context + added < new_count { + let (rest, kind) = alt(( + // +added line + map(preceded(char('+'), not_line_ending), |s: Input<'a>| LineKind::Add(*s.fragment())), + // -removed line + map(preceded(char('-'), not_line_ending), |s: Input<'a>| LineKind::Remove(*s.fragment())), + // ' ' context line (possibly empty after the space) + map(preceded(char(' '), opt(not_line_ending)), |opt_s: Option>| { + LineKind::Context(opt_s.map(|s| *s.fragment()).unwrap_or("")) + }), + // bare newline (no prefix at all) = empty context line + map(line_ending, |_| LineKind::EmptyContext), + ))(input)?; + + // For all but EmptyContext, consume the line ending + + let (rest, _) = match &kind { + LineKind::EmptyContext => (rest, ()), // already consumed + _ => { + let (rest, _) = line_ending(rest)?; + (rest, ()) + } + }; + // Update counters and build Line + let line = match kind { + LineKind::Add(s) => { + added += 1; + Line::Add(s) + } + LineKind::Remove(s) => { + removed += 1; + Line::Remove(s) + } + LineKind::Context(s) => { + context += 1; + Line::Context(s) + } + LineKind::EmptyContext => { + context += 1; + Line::Context("") + } + }; + + lines.push(line); + input = rest; + + if context + removed == old_count && context + added == new_count { + break; + } + } + + Ok((input, lines)) +} + fn chunk_header(input: Input<'_>) -> IResult, (Range, Range, &'_ str)> { let (input, _) = tag("@@ -")(input)?; let (input, old_range) = range(input)?; diff --git a/tests/parse_patch.rs b/tests/parse_patch.rs index da25ada..982c2f6 100644 --- a/tests/parse_patch.rs +++ b/tests/parse_patch.rs @@ -240,7 +240,6 @@ fn test_parse_triple_plus_minus() -> Result<(), ParseError<'static>> { // actually takes the hunk ranges into account, the #[should_panic] annotation should be removed. // See the FIXME comment on top of the chunk_line parser. #[test] -#[should_panic] fn test_parse_triple_plus_minus_hack() { // Our parser has some hacky rules to make sure that lines starting with +++ or --- aren't // interpreted as regular addition/removal lines that could be part of a hunk. This test diff --git a/tests/parse_samples.rs b/tests/parse_samples.rs index 50e223a..d740cc6 100644 --- a/tests/parse_samples.rs +++ b/tests/parse_samples.rs @@ -46,9 +46,10 @@ fn parse_wild_samples() { } let data = fs::read_to_string(dbg!(&path)).unwrap(); - let patches = Patch::from_multiple(&data) - .unwrap_or_else(|err| panic!("failed to parse {:?}, error: {}", path, err)); + let patches = Patch::from_multiple(&data); + println!("Patches: {:?}", patches); + let patches = patches.unwrap_or_else(|err| panic!("failed to parse {:?}, error: {}", path, err)); // Make sure that the patch file we produce parses to the same information as the original // patch file. #[allow(clippy::format_collect)] // Display::fmt is the only way to resolve Patch->str diff --git a/tests/wild-samples/sample2.patch b/tests/wild-samples/sample2.patch new file mode 100644 index 0000000..857997d --- /dev/null +++ b/tests/wild-samples/sample2.patch @@ -0,0 +1,53 @@ +# HG changeset patch +# Parent 13ba6cbdb304cd251fbc22466cadb21019ee817f +# User Bill McCloskey + +diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp +--- a/content/base/src/nsContentUtils.cpp ++++ b/content/base/src/nsContentUtils.cpp +@@ -6369,17 +6369,17 @@ public: + nsCycleCollectionParticipant* helper) + { + } + + NS_IMETHOD_(void) NoteNextEdgeName(const char* name) + { + } + +- NS_IMETHOD_(void) NoteWeakMapping(void* map, void* key, void* val) ++ NS_IMETHOD_(void) NoteWeakMapping(void* map, void* key, void* kdelegate, void* val) + { + } + + bool mFound; + + private: + void* mWrapper; + }; +diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp +--- a/js/src/jsfriendapi.cpp ++++ b/js/src/jsfriendapi.cpp +@@ -527,16 +527,24 @@ js::VisitGrayWrapperTargets(JSCompartmen + { + for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) { + gc::Cell *thing = e.front().key.wrapped; + if (thing->isMarked(gc::GRAY)) + callback(closure, thing); + } + } + ++JS_FRIEND_API(JSObject *) ++js::GetWeakmapKeyDelegate(JSObject *key) ++{ ++ if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) ++ return op(key); ++ return NULL; ++} ++ + JS_FRIEND_API(void) + JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) + { + rt->telemetryCallback = callback; + } + + JS_FRIEND_API(JSObject *)