diff --git a/src/patch.ml b/src/patch.ml index b7bfa28..8857a25 100644 --- a/src/patch.ml +++ b/src/patch.ml @@ -161,20 +161,60 @@ let operation_eq a b = match a, b with let no_file = "/dev/null" +let pp_filename ppf fn = + (* NOTE: filename quote format from GNU diffutils *) + let rec aux ~to_quote buf fn ~len i = + if i < len then + let c = fn.[i] in + let to_quote = + if c = '\007' then + (Buffer.add_string buf "\\a"; true) + else if c = '\b' then + (Buffer.add_string buf "\\b"; true) + else if c = '\t' then + (Buffer.add_string buf "\\t"; true) + else if c = '\n' then + (Buffer.add_string buf "\\n"; true) + else if c = '\011' then + (Buffer.add_string buf "\\v"; true) + else if c = '\012' then + (Buffer.add_string buf "\\f"; true) + else if c = '\r' then + (Buffer.add_string buf "\\r"; true) + else if c < ' ' || c > '~' then + (Printf.bprintf buf "\\%03o" (Char.code c); true) + else if c = ' ' then + (Buffer.add_char buf ' '; true) + else if c = '"' || c = '\\' then + (Buffer.add_char buf '\\'; Buffer.add_char buf c; true) + else + (Buffer.add_char buf c; to_quote) + in + aux ~to_quote buf fn ~len (i + 1) + else + to_quote + in + let len = String.length fn in + let buf = Buffer.create (len * 2) in + if aux ~to_quote:false buf fn ~len 0 then + Format.fprintf ppf "\"%s\"" (Buffer.contents buf) + else + Format.pp_print_text ppf fn + let pp_operation ppf op = match op with | Edit (old_name, new_name) -> - Format.fprintf ppf "--- %s\n" old_name ; - Format.fprintf ppf "+++ %s\n" new_name + Format.fprintf ppf "--- %a\n" pp_filename old_name ; + Format.fprintf ppf "+++ %a\n" pp_filename new_name | Delete name -> - Format.fprintf ppf "--- %s\n" name ; - Format.fprintf ppf "+++ %s\n" no_file + Format.fprintf ppf "--- %a\n" pp_filename name ; + Format.fprintf ppf "+++ %a\n" pp_filename no_file | Create name -> - Format.fprintf ppf "--- %s\n" no_file ; - Format.fprintf ppf "+++ %s\n" name + Format.fprintf ppf "--- %a\n" pp_filename no_file ; + Format.fprintf ppf "+++ %a\n" pp_filename name | Rename_only (old_name, new_name) -> - Format.fprintf ppf "rename from %s\n" old_name; - Format.fprintf ppf "rename to %s\n" new_name + Format.fprintf ppf "rename from %a\n" pp_filename old_name; + Format.fprintf ppf "rename to %a\n" pp_filename new_name type t = { operation : operation ; diff --git a/test/test.ml b/test/test.ml index cf4962e..e480c55 100644 --- a/test/test.ml +++ b/test/test.ml @@ -1011,6 +1011,38 @@ let patch_p = [ "-p1 with root files", `Quick, p1_root; ] +let pp_output_test = Alcotest.testable Format.pp_print_string String.equal +let operations exp str () = + let exp = Format.asprintf "%a" Patch.pp_operation exp in + Alcotest.(check pp_output_test) __LOC__ str exp + +let plain_filename = +{|--- a/test ++++ b/test +|} + +let plain_filename = operations (Patch.Edit ("a/test", "b/test")) plain_filename + +let filename_with_spaces = +{|--- "a/one space" ++++ "b/with two spaces" +|} + +let filename_with_spaces = operations (Patch.Edit ("a/one space", "b/with two spaces")) filename_with_spaces + +let filename_with_special_chars = +{|--- "\a\b\f\n\r\t\v some name \\\"\001\177&" ++++ /dev/null +|} + +let filename_with_special_chars = operations (Patch.Delete "\007\b\012\n\r\t\011 some name \\\"\001\127&") filename_with_special_chars + +let pp_filenames = [ + "plain", `Quick, plain_filename; + "with spaces", `Quick, filename_with_spaces; + "with special characters", `Quick, filename_with_special_chars; +] + let tests = [ "parse", parse_diffs ; "apply", apply_diffs ; @@ -1021,6 +1053,7 @@ let tests = [ "regression", regression_diffs ; "diff", unified_diff_creation ; "patch -p", patch_p; + "pretty-print filenames", pp_filenames; ] let () =