├── README.md ├── .gitignore ├── src ├── diffy.app.src ├── diffy_term.erl ├── diffy_simple_patch.erl └── diffy.erl ├── rebar.config ├── Makefile ├── LICENSE └── test └── diffy_tests.erl /README.md: -------------------------------------------------------------------------------- 1 | # Diffy 2 | 3 | Moved to https://github.com/zotonic/diffy 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rebar 2 | ebin 3 | .eunit 4 | deps 5 | *.o 6 | *.beam 7 | *.plt 8 | erl_crash.dump 9 | tags 10 | -------------------------------------------------------------------------------- /src/diffy.app.src: -------------------------------------------------------------------------------- 1 | {application, diffy, 2 | [ 3 | {description, "Diff, match patch implementation."}, 4 | {vsn, "0.1"}, 5 | {registered, []}, 6 | {applications, [kernel, stdlib]}, 7 | {env, []} 8 | ]}. 9 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info, warn_unused_vars, warn_shadow_vars]}. 2 | {deps, [ 3 | {z_stdlib, ".*", {git, "git://github.com/zotonic/z_stdlib.git", {branch, "0.x"}}}, 4 | 5 | %% For testing 6 | {proper, "", {git, "git://github.com/manopapad/proper.git", {branch, "master"}}} 7 | ]}. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = diffy 2 | REBAR := $(shell which rebar 2>/dev/null || echo ./rebar) 3 | REBAR_URL := https://github.com/downloads/basho/rebar/rebar 4 | DIALYZER = dialyzer 5 | 6 | all: compile 7 | 8 | ./rebar: 9 | erl -noshell -s inets start -s ssl start \ 10 | -eval '{ok, saved_to_file} = httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar"}])' \ 11 | -s inets stop -s init stop 12 | chmod +x ./rebar 13 | 14 | compile: rebar 15 | $(REBAR) compile 16 | 17 | eunit: rebar 18 | $(REBAR) get-dep compile 19 | $(REBAR) eunit -v skip_deps=true 20 | 21 | clean: rebar 22 | $(REBAR) clean 23 | 24 | distclean: 25 | rm $(REBAR) 26 | 27 | 28 | # dializer 29 | 30 | build-plt: 31 | @$(DIALYZER) --build_plt --output_plt .$(PROJECT).plt \ 32 | --apps erts kernel stdlib -r deps 33 | 34 | dialyze: 35 | @$(DIALYZER) -pa deps/*/ebin --src src --plt .$(PROJECT).plt --no_native \ 36 | -Werror_handling -Wrace_conditions -Wunmatched_returns 37 | -------------------------------------------------------------------------------- /src/diffy_term.erl: -------------------------------------------------------------------------------- 1 | %% @author Maas-Maarten Zeeman 2 | %% @copyright 2014 Maas-Maarten Zeeman 3 | %% 4 | %% @doc Diffy, an erlang diff match and patch implementation 5 | %% Adapted from diffy.erl for simple diff on a list of Erlang terms 6 | %% 7 | %% Copyright 2014-2015 Maas-Maarten Zeeman, Marc Worrell 8 | %% 9 | %% Licensed under the Apache License, Version 2.0 (the "License"); 10 | %% you may not use this file except in compliance with the License. 11 | %% You may obtain a copy of the License at 12 | %% 13 | %% http://www.apache.org/licenses/LICENSE-2.0 14 | %% 15 | %% Unless required by applicable law or agreed to in writing, software 16 | %% distributed under the License is distributed on an "AS IS" BASIS, 17 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | %% See the License for the specific language governing permissions and 19 | %% limitations under the License. 20 | %% Erlang diff-match-patch implementation 21 | 22 | -module(diffy_term). 23 | 24 | -export([ 25 | diff/2 26 | ]). 27 | 28 | -type diff_op() :: delete | equal | insert. 29 | -type diff() :: {diff_op(), unicode:unicode_binary()}. 30 | -type diffs() :: list(diff()). 31 | 32 | -export_type([diffs/0]). 33 | 34 | -spec diff(list(), list()) -> diffy:diffs(). 35 | diff(A, A) -> 36 | [{equal, A}]; 37 | diff(A, []) -> 38 | [{delete, A}]; 39 | diff([], B) -> 40 | [{insert, B}]; 41 | diff(A, B) when is_list(A), is_list(B) -> 42 | {Dict0, N} = term_dict(A, dict:new(), 0), 43 | {Dict, _N} = term_dict(B, Dict0, N), 44 | Diff = diffy:diff(map_terms(A, Dict), map_terms(B, Dict)), 45 | unmap_diff(Diff, Dict). 46 | 47 | term_dict([], D, N) -> 48 | {D, N}; 49 | term_dict([T|Ts], D, N) -> 50 | case dict:is_key(T, D) of 51 | true -> 52 | term_dict(Ts, D, N); 53 | false -> 54 | D1 = dict:store(T, N, D), 55 | term_dict(Ts, D1, N+1) 56 | end. 57 | 58 | map_terms(A, Dict) -> 59 | T = [ dict:fetch(K, Dict) || K <- A ], 60 | unicode:characters_to_binary(T). 61 | 62 | unmap_diff(Ds, Dict) -> 63 | RDict = dict:from_list([ {N,T} || {T,N} <- dict:to_list(Dict) ]), 64 | [ unmap_diff_1(D, RDict) || D <- Ds ]. 65 | 66 | unmap_diff_1({Op, B}, RDict) -> 67 | Cs = unicode:characters_to_list(B), 68 | {Op, [ dict:fetch(C, RDict) || C <- Cs ]}. 69 | 70 | 71 | 72 | -ifdef(TEST). 73 | 74 | -include_lib("eunit/include/eunit.hrl"). 75 | 76 | diffy_term_test() -> 77 | ?assertEqual( 78 | [{equal,[a]}], 79 | diffy_term:diff([a], [a])), 80 | ?assertEqual( 81 | [{insert,[a]}], 82 | diffy_term:diff([], [a])), 83 | ?assertEqual( 84 | [{delete,[a]}], 85 | diffy_term:diff([a], [])), 86 | ?assertEqual( 87 | [{equal,[a]},{insert,[e]},{equal,[b,c,d]},{delete,[e]}], 88 | diffy_term:diff([a,b,c,d,e], [a,e,b,c,d])), 89 | ok. 90 | 91 | 92 | -endif. 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/diffy_simple_patch.erl: -------------------------------------------------------------------------------- 1 | %% @author Maas-Maarten Zeeman 2 | %% @copyright 2014 Maas-Maarten Zeeman 3 | %% 4 | %% @doc Diffy, an erlang diff match and patch implementation 5 | %% 6 | %% Copyright 2014 Maas-Maarten Zeeman 7 | %% 8 | %% Licensed under the Apache License, Version 2.0 (the "License"); 9 | %% you may not use this file except in compliance with the License. 10 | %% You may obtain a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, software 15 | %% distributed under the License is distributed on an "AS IS" BASIS, 16 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | %% See the License for the specific language governing permissions and 18 | %% limitations under the License. 19 | %% Erlang diff-match-patch implementation 20 | 21 | -module(diffy_simple_patch). 22 | 23 | -export([make_patch/1, apply_patch/2]). 24 | 25 | 26 | %% Make a simple patch with edit operations. 27 | %% 28 | make_patch(Diffs) -> 29 | make_patch(Diffs, []). 30 | 31 | %% @doc Make a simple patch. Removes the data from equal and delete 32 | %% operations and replaces it with a line and char count. 33 | make_patch([], [{copy, _}]) -> 34 | []; 35 | make_patch([], Acc) -> 36 | lists:reverse(Acc); 37 | make_patch([{insert, _}=H|Rest], Acc) -> 38 | make_patch(Rest, [H|Acc]); 39 | make_patch([{Op, Data}|Rest], Acc) -> 40 | make_patch(Rest, [{patch_op(Op), size(Data)}|Acc]). 41 | 42 | patch_op(delete) -> skip; 43 | patch_op(equal) -> copy. 44 | 45 | % @doc Use the SourceText to reconstruct the destination text. 46 | apply_patch(SourceText, Diffs) -> 47 | apply_patch(SourceText, 0, Diffs, []). 48 | 49 | apply_patch(_SourceText, _Idx, [], Acc) -> 50 | erlang:iolist_to_binary(lists:reverse(Acc)); 51 | apply_patch(SourceText, Idx, [{insert, Data}|Rest], Acc) -> 52 | apply_patch(SourceText, Idx, Rest, [Data|Acc]); 53 | 54 | %% New simple interface, just use the binary length. 55 | apply_patch(SourceText, Idx, [{copy, BinLen}|Rest], Acc) when Idx + BinLen =< size(SourceText) -> 56 | <<_:Idx/binary, Data:BinLen/binary, _Rest/binary>> = SourceText, 57 | apply_patch(SourceText, Idx+BinLen, Rest, [Data|Acc]); 58 | apply_patch(SourceText, Idx, [{skip, BinLen}|Rest], Acc) when Idx + BinLen =< size(SourceText) -> 59 | apply_patch(SourceText, Idx+BinLen, Rest, Acc); 60 | 61 | %% Old interface which counts lines and remaining chars. 62 | apply_patch(SourceText, Idx, [{copy, Lines, Chars}|Rest], Acc) -> 63 | LineData = get_lines(SourceText, Idx, Lines), 64 | CharData = get_chars(SourceText, Idx+size(LineData), Chars), 65 | %% Get the data from the source-text. 66 | apply_patch(SourceText, Idx+size(LineData)+size(CharData), Rest, [CharData, LineData|Acc]); 67 | apply_patch(SourceText, Idx, [{skip, Lines, Chars}|Rest], Acc) -> 68 | LineData = get_lines(SourceText, Idx, Lines), 69 | CharData = get_chars(SourceText, Idx+size(LineData), Chars), 70 | %% Advance the index 71 | apply_patch(SourceText, Idx+size(LineData)+size(CharData), Rest, Acc). 72 | 73 | %% Get the data from N lines. 74 | get_lines(Source, Idx, Lines) -> 75 | get_lines(Source, Idx, Lines, <<>>). 76 | 77 | get_lines(_Source, _Idx, 0, Acc) -> 78 | Acc; 79 | get_lines(Source, Idx, Lines, Acc) -> 80 | case binary:match(Source, <<"\n">>, [{scope, {Idx, size(Source) - Idx}}]) of 81 | nomatch -> 82 | Acc; 83 | {Start, _} -> 84 | LineSize = Start-Idx+1, 85 | <<_:Idx/binary, Line:LineSize/binary, _Rest/binary>> = Source, 86 | get_lines(Source, Start+1, Lines-1, <>) 87 | end. 88 | 89 | %% @doc Get the chardata for NChars from Source. 90 | get_chars(Source, Idx, Chars) -> 91 | get_chars(Source, Idx, Chars, <<>>). 92 | 93 | get_chars(_Source, _Idx, 0, CharData) -> 94 | CharData; 95 | get_chars(Source, Idx, Chars, CharData) -> 96 | <<_:Idx/binary, C/utf8, _/binary>> = Source, 97 | S = size(<>), 98 | get_chars(Source, Idx+S, Chars-1, <>). 99 | 100 | 101 | %% 102 | %% Tests 103 | %% 104 | 105 | -ifdef(TEST). 106 | 107 | -include_lib("eunit/include/eunit.hrl"). 108 | 109 | make_patch_test() -> 110 | ?assertEqual([], make_patch([])), 111 | ?assertEqual([], make_patch([{equal, <<"foo">>}])), 112 | ?assertEqual([{copy, 4}, {insert, <<"test">>}], 113 | make_patch([{equal, <<"foo\n">>}, {insert, <<"test">>}])), 114 | 115 | ok. 116 | 117 | get_lines_test() -> 118 | ?assertEqual(<<"hoi\n">>, get_lines(<<"hoi\n">>, 0, 1)), 119 | ?assertEqual(<<"hoi\n\n">>, get_lines(<<"hoi\n\n">>, 0, 2)), 120 | ?assertEqual(<<"oi\n\n">>, get_lines(<<"hoi\n\n">>, 1, 2)), 121 | ok. 122 | 123 | get_chars_test() -> 124 | ?assertEqual(<<"h">>, get_chars(<<"hoi\n">>, 0, 1)), 125 | ?assertEqual(<<"oi">>, get_chars(<<"hoi\n">>, 1, 2)), 126 | ?assertEqual(<<"i\n">>, get_chars(<<"hoi\n">>, 2, 2)), 127 | ok. 128 | 129 | apply_patch_test() -> 130 | A = <<"the cat\n\n is in the hat\n">>, 131 | B = <<"the rabbit eats\n\n a carrot\n in the hat\n">>, 132 | 133 | Diffs = diffy:diff(A, B), 134 | CDiffs = diffy:cleanup_efficiency(Diffs), 135 | Patch = make_patch(CDiffs), 136 | 137 | %% Check if the transformation worked. 138 | %% 139 | ?assertEqual(B, erlang:iolist_to_binary(apply_patch(A, Patch))), 140 | 141 | ok. 142 | 143 | 144 | -endif. 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /test/diffy_tests.erl: -------------------------------------------------------------------------------- 1 | %% @author Maas-Maarten Zeeman 2 | %% @copyright 2014 Maas-Maarten Zeeman 3 | %% 4 | %% @doc Diffy, an erlang diff match and patch implementation 5 | %% 6 | %% Copyright 2014 Maas-Maarten Zeeman 7 | %% 8 | %% Licensed under the Apache License, Version 2.0 (the "License"); 9 | %% you may not use this file except in compliance with the License. 10 | %% You may obtain a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, software 15 | %% distributed under the License is distributed on an "AS IS" BASIS, 16 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | %% See the License for the specific language governing permissions and 18 | %% limitations under the License. 19 | %% Erlang diff-match-patch implementation 20 | 21 | -module(diffy_tests). 22 | 23 | -include_lib("proper/include/proper.hrl"). 24 | -include_lib("eunit/include/eunit.hrl"). 25 | 26 | %% 27 | %% Properties 28 | %% 29 | 30 | prop_cleanup_merge() -> 31 | ?FORALL(Diffs, diffy:diffs(), 32 | begin 33 | SourceText = diffy:source_text(Diffs), 34 | DestinationText = diffy:destination_text(Diffs), 35 | 36 | CleanDiffs = cleanup_merge(Diffs), 37 | 38 | SourceText == diffy:source_text(CleanDiffs) andalso 39 | DestinationText == diffy:destination_text(CleanDiffs) 40 | end). 41 | 42 | prop_cleanup_efficiency() -> 43 | ?FORALL(Diffs, diffy:diffs(), 44 | begin 45 | SourceText = diffy:source_text(Diffs), 46 | DestinationText = diffy:destination_text(Diffs), 47 | 48 | EfficientDiffs = cleanup_efficiency(Diffs), 49 | 50 | SourceText == diffy:source_text(EfficientDiffs) andalso 51 | DestinationText == diffy:destination_text(EfficientDiffs) 52 | end). 53 | 54 | html_like() -> 55 | proper_types:resize(200, 56 | list(frequency([{80, range($a, $z)}, % letters 57 | {20, oneof(["&", ">", "