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 *)