Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions diff.carp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ Expects the output of `line-diff` or a diff of string arrays.
Uses 3 lines of context around changes.")
(defn unified [d] (unified-with-context d 3))

; core's starts-with?/suffix guard on byte length but index characters; patch input is bytes
(defn- byte-starts-with? [s sub]
(let [ls (String.length sub)]
(and (>= (String.length s) ls) (= sub &(String.byte-slice s 0 ls)))))

(defn- byte-suffix [s b] (String.byte-slice s b (String.length s)))

; parses the 1-based old-file start line from a unified hunk header such as
; `@@ -3,4 +3,5 @@`; the count is optional (`@@ -3 +3 @@` means one line).
(defn- parse-hunk-start [header]
Expand All @@ -273,9 +280,9 @@ Uses 3 lines of context around changes.")
(for [i 0 (Array.length &tokens)]
(let [tok (Array.unsafe-nth &tokens i)]
(when (and (Maybe.nothing? &res)
(String.starts-with? tok "-")
(byte-starts-with? tok "-")
(> (String.length tok) 1))
(let [range (String.suffix tok 1)]
(let [range (byte-suffix tok 1)]
(set! res
(Int.from-string
(Array.unsafe-nth &(String.split-by &range &[\,]) 0)))))))
Expand All @@ -300,7 +307,7 @@ or out of order. Both documents are split and rejoined on newlines, so
(unless failed
(let [line (Array.unsafe-nth &lines i)]
(cond
(String.starts-with? line "@@")
(byte-starts-with? line "@@")
(match (parse-hunk-start line)
(Maybe.Nothing) (set! failed true)
(Maybe.Just start)
Expand All @@ -311,22 +318,22 @@ or out of order. Both documents are split and rejoined on newlines, so
(for [k cursor target]
(Array.push-back! &out @(Array.unsafe-nth &orig k)))
(set! cursor target)))))
(String.starts-with? line " ")
(let [content (String.suffix line 1)]
(byte-starts-with? line " ")
(let [content (byte-suffix line 1)]
(if (or (>= cursor olen)
(/= (Array.unsafe-nth &orig cursor) &content))
(set! failed true)
(do
(Array.push-back! &out content)
(set! cursor (Int.inc cursor)))))
(String.starts-with? line "-")
(let [content (String.suffix line 1)]
(byte-starts-with? line "-")
(let [content (byte-suffix line 1)]
(if (or (>= cursor olen)
(/= (Array.unsafe-nth &orig cursor) &content))
(set! failed true)
(set! cursor (Int.inc cursor))))
(String.starts-with? line "+")
(Array.push-back! &out (String.suffix line 1))
(byte-starts-with? line "+")
(Array.push-back! &out (byte-suffix line 1))
()))))
(unless failed
(for [k cursor olen] (Array.push-back! &out @(Array.unsafe-nth &orig k))))
Expand Down
31 changes: 30 additions & 1 deletion tests/diff.carp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

(use-all Diff Test)

; 0xBB is `»` in latin-1: one byte, zero utf-8 characters
(defn latin1-raquo [] (String.from-bytes &[187b]))

(defn main []
(with-test test
(assert-equal test
Expand Down Expand Up @@ -498,4 +501,30 @@ c"))) "patch returns Nothing when a context line does not match the original")
(assert-equal test &(the (Maybe String) (Maybe.Nothing)) &(patch "a
b" "@@ garbage @@
a
") "patch returns Nothing on a malformed hunk header")))
") "patch returns Nothing on a malformed hunk header")
(assert-equal test &(Maybe.Just @"a
b") &(patch "a
b" "@@ -1,2 +1,2 @@
a
é
b") "patch does not abort on a line that is a single multi-byte character")
(assert-equal test &(Maybe.Just @"a
b") &(patch "a
b" &(String.concat &[@"@@ -1,2 +1,2 @@
a
" (latin1-raquo) @"
b"])) "patch does not abort on a body line that is a single continuation byte")
(assert-equal test &(the (Maybe String) (Maybe.Nothing)) &(patch "a
b" &(String.concat &[@"@@ " (latin1-raquo) @" @@
a
"])) "patch does not abort on a continuation byte in the hunk header")
(assert-equal test &(Maybe.Just (String.concat &[(latin1-raquo) @"
b"])) &(patch &(String.concat &[(latin1-raquo) @"
b"]) &(String.concat &[@"@@ -1,2 +1,2 @@
" (latin1-raquo) @"
b"])) "patch applies a no-op patch to a latin-1 document")
(assert-equal test &(Maybe.Just (String.concat &[(latin1-raquo) @"
B"])) &(patch &(String.concat &[(latin1-raquo) @"
b"]) &(unified &(line-diff &(String.concat &[(latin1-raquo) @"
b"]) &(String.concat &[(latin1-raquo) @"
B"])))) "patch round-trips a diff of a latin-1 document")))