2424 error_info /3 ,
2525 compute /2 , compute /3 ,
2626 to_algebra /2 ,
27- to_diff /3
27+ to_diff /3 ,
28+ to_sides /1
2829]).
2930
3031-export ([red /0 , green /0 , yellow /0 , reset /0 ]).
3334
3435-export ([glue /3 , join_docs /2 ]).
3536
36- -export_type ([supported_input / 0 ]).
37+ -export_type ([supported_input / 0 , diff / 0 , side / 0 ]).
3738
38- -type diff () :: #{ equivalent : = boolean (), left : = side (), right : = side ()}.
39+ -type diff () :: { eq , supported_input ()} | { diff , side (), side ()}.
3940-type side () ::
4041 contents ()
41- | literal ()
42+ | { literal , literal ()}
4243 | {container_type (), meta (), [side ()]}
4344 | block ()
44- | {struct_type (), meta (), [side ()]}
45- | struct_item ().
45+ | {struct_type (), meta (), [struct_arg ()]}
46+ | struct_item ()
47+ | {eq , supported_input ()}.
48+ % % A struct (map/record) carries either a `{Key, ValueSide}` pair for keys
49+ % % present on both sides, or a whole-item `block()` for keys present on one side.
50+ -type struct_arg () :: {supported_input (), side ()} | block ().
4651-type contents () :: #{contents := [content ()]}.
4752-type content () :: {boolean (), binary ()}.
4853-type literal () :: number () | atom () | binary () | string ().
49- -type block () :: {'__block__' , meta (), [supported_input ()]}.
54+ -type block () :: {'__block__' , meta (), [side ()]}.
5055-type meta () :: [{diff , boolean ()}].
5156-type script () :: diffy :diffs ().
5257-type doc () :: erlfmt_algebra :doc ().
@@ -72,9 +77,9 @@ error_info(Module, Left0, Right0) ->
7277 Ref = make_ref (),
7378 spawn (
7479 fun () ->
75- Diff = compute (Left0 , Right0 , Module ),
76- {ok , Left } = to_diff (maps : get ( left , Diff ) , red (), reset ()),
77- {ok , Right } = to_diff (maps : get ( right , Diff ) , green (), reset ()),
80+ { LeftSide , RightSide } = to_sides ( compute (Left0 , Right0 , Module ) ),
81+ {ok , Left } = to_diff (LeftSide , red (), reset ()),
82+ {ok , Right } = to_diff (RightSide , green (), reset ()),
7883 Cause = #{left => Left , right => Right },
7984 Self ! {Ref , #{module => ? MODULE , function => format_error , cause => Cause }}
8085 end
@@ -149,6 +154,12 @@ compute(Left, Right, Module) ->
149154to_algebra (Side , DiffWrapper ) ->
150155 wrap_on_diff (Side , fun safe_to_algebra /2 , DiffWrapper ).
151156
157+ -spec to_sides (diff ()) -> {side (), side ()}.
158+ to_sides ({eq , Same }) ->
159+ {{eq , Same }, {eq , Same }};
160+ to_sides ({diff , Left , Right }) ->
161+ {Left , Right }.
162+
152163-spec format_error (term (), erlang :stacktrace ()) -> error_description ().
153164format_error (Reason , [{_M , _F , _Args , Info } | _ ]) ->
154165 ErrorInfo = proplists :get_value (error_info , Info , #{}),
@@ -162,7 +173,7 @@ format_error(Reason, [{_M, _F, _Args, Info} | _]) ->
162173
163174-spec diff_value (supported_input (), supported_input (), context ()) -> diff ().
164175diff_value (Same , Same , _Context ) ->
165- #{ equivalent => true , left => Same , right => Same };
176+ { eq , Same };
166177diff_value (Left , Right , _Context ) when is_number (Left ), is_number (Right ) ->
167178 diff_literal (Left , Right );
168179diff_value (Left , Right , _Context ) when is_atom (Left ), is_atom (Right ) ->
@@ -199,9 +210,9 @@ is_printable(Binary) ->
199210
200211-spec non_recursive_diff_value (supported_input (), supported_input ()) -> diff ().
201212non_recursive_diff_value (Left0 , Right0 ) ->
202- Left = update_diff_meta (Left0 , true ),
203- Right = update_diff_meta (Right0 , true ),
204- #{ equivalent => false , left => Left , right => Right }.
213+ Left = update_diff_meta (value_to_side ( Left0 ) , true ),
214+ Right = update_diff_meta (value_to_side ( Right0 ) , true ),
215+ { diff , Left , Right }.
205216
206217-spec diff_literal (literal (), literal ()) -> diff ().
207218diff_literal (Left , Right ) ->
@@ -210,42 +221,36 @@ diff_literal(Left, Right) ->
210221-spec diff_string (binary (), binary ()) -> diff ().
211222diff_string (Left , Right ) ->
212223 Script = diffy :diff (Left , Right ),
213- string_script_to_diff (Script , true , [], []).
224+ string_script_to_diff (Script , [], []).
214225
215226-spec diff_container (container_type (), [supported_input ()], [supported_input ()], context ()) ->
216227 diff ().
217228diff_container (Type , Left , Right , Context ) ->
218- diff_container (Type , Left , Right , true , [], [], Context ).
229+ diff_container (Type , Left , Right , [], [], Context ).
219230
220231-spec diff_container (
221232 container_type (),
222233 [supported_input ()],
223234 [supported_input ()],
224- boolean (),
225235 [side ()],
226236 [side ()],
227237 context ()
228238) ->
229239 diff ().
230- diff_container (Type , [HLeft | TLeft ], [HRight | TRight ], Equivalent0 , AccLeft , AccRight , Context ) ->
231- #{ equivalent : = Equivalent , left : = Left , right : = Right } = diff_value (HLeft , HRight , Context ),
240+ diff_container (Type , [HLeft | TLeft ], [HRight | TRight ], AccLeft , AccRight , Context ) ->
241+ { Left , Right } = to_sides ( diff_value (HLeft , HRight , Context ) ),
232242 diff_container (
233243 Type ,
234244 TLeft ,
235245 TRight ,
236- Equivalent0 andalso Equivalent ,
237246 [Left | AccLeft ],
238247 [Right | AccRight ],
239248 Context
240249 );
241- diff_container (Type , Left0 , Right0 , Equivalent , AccLeft , AccRight , _Context ) ->
242- Left = [update_diff_meta (L , true ) || L <- Left0 ],
243- Right = [update_diff_meta (L , true ) || L <- Right0 ],
244- #{
245- equivalent => Equivalent andalso Left =:= [] andalso Right =:= [],
246- left => {Type , [], lists :reverse (AccLeft , Left )},
247- right => {Type , [], lists :reverse (AccRight , Right )}
248- }.
250+ diff_container (Type , Left0 , Right0 , AccLeft , AccRight , _Context ) ->
251+ Left = [update_diff_meta (value_to_side (L ), true ) || L <- Left0 ],
252+ Right = [update_diff_meta (value_to_side (L ), true ) || L <- Right0 ],
253+ {diff , {Type , [], lists :reverse (AccLeft , Left )}, {Type , [], lists :reverse (AccRight , Right )}}.
249254
250255-spec diff_struct (
251256 struct_type (),
@@ -255,38 +260,40 @@ diff_container(Type, Left0, Right0, Equivalent, AccLeft, AccRight, _Context) ->
255260) -> diff ().
256261diff_struct (Type , Left0 , Right0 , Context ) ->
257262 Acc0 = {
258- _Equivalent = true ,
259263 _AccLeft = [],
260264 _AccRight = [],
261265 _PendingLeft = [],
262266 _PendingRight = Right0
263267 },
264- Fun = fun (LeftKey , LeftValue , {Equivalent0 , AccLeft , AccRight , PendingLeft0 , PendingRight0 }) ->
268+ Fun = fun (LeftKey , LeftValue , {AccLeft , AccRight , PendingLeft0 , PendingRight0 }) ->
265269 case maps :take (LeftKey , PendingRight0 ) of
266270 error ->
267271 PendingLeft = [
268- update_diff_meta ({struct_item , LeftKey , divider (Type ), LeftValue }, true )
272+ update_diff_meta (
273+ {struct_item , value_to_side (LeftKey ), divider (Type ), value_to_side (LeftValue )},
274+ true
275+ )
269276 | PendingLeft0
270277 ],
271- {false , AccLeft , AccRight , PendingLeft , PendingRight0 };
278+ {AccLeft , AccRight , PendingLeft , PendingRight0 };
272279 {RightValue , PendingRight } ->
273- Diff = diff_value (LeftValue , RightValue , Context ),
274- #{equivalent := Equivalent , left := Left , right := Right } = Diff ,
280+ {Left , Right } = to_sides (diff_value (LeftValue , RightValue , Context )),
275281 {
276- Equivalent0 andalso Equivalent ,
277282 [{LeftKey , Left } | AccLeft ],
278283 [{LeftKey , Right } | AccRight ],
279284 PendingLeft0 ,
280285 PendingRight
281286 }
282287 end
283288 end ,
284- {AccEquivalent , AccLeft , AccRight , PendingLeft , PendingRight0 } = maps :fold (Fun , Acc0 , Left0 ),
285- PendingRight = [update_diff_meta ({struct_item , K , divider (Type ), V }, true ) || K := V <- PendingRight0 ],
286- Equivalent = AccEquivalent andalso (maps :size (PendingRight0 ) =:= 0 ),
289+ {AccLeft , AccRight , PendingLeft , PendingRight0 } = maps :fold (Fun , Acc0 , Left0 ),
290+ PendingRight = [
291+ update_diff_meta ({struct_item , value_to_side (K ), divider (Type ), value_to_side (V )}, true )
292+ || K := V <- PendingRight0
293+ ],
287294 Left = lists :sort (AccLeft ) ++ lists :sort (PendingLeft ),
288295 Right = lists :sort (AccRight ) ++ lists :sort (PendingRight ),
289- #{ equivalent => Equivalent , left => {Type , [], Left }, right => {Type , [], Right }}.
296+ { diff , {Type , [], Left }, {Type , [], Right }}.
290297
291298-spec divider (struct_type ()) -> delimiter ().
292299divider (map ) -> <<" => " >>;
@@ -313,10 +320,18 @@ expand_tuple([Name | Values], #{records := Records} = _Context) when is_atom(Nam
313320expand_tuple (_ , _ ) ->
314321 not_found .
315322
316- -spec update_diff_meta (supported_input (), boolean ()) -> block ().
323+ -spec update_diff_meta (side (), boolean ()) -> block ().
317324update_diff_meta (Value , true ) ->
318325 {'__block__' , [{diff , true }], [Value ]}.
319326
327+ % % Wrap a raw value as a leaf side(): scalars become tagged literals,
328+ % % compound terms (tuples, maps) are rendered whole via the `eq` tag.
329+ -spec value_to_side (supported_input ()) -> side ().
330+ value_to_side (Value ) when is_number (Value ); is_atom (Value ); is_binary (Value ) ->
331+ {literal , Value };
332+ value_to_side (Value ) ->
333+ {eq , Value }.
334+
320335-spec inspect (term ()) -> binary ().
321336inspect (Term ) ->
322337 inspect (Term , " ~0tp " ).
@@ -357,14 +372,16 @@ safe_to_algebra(#{contents := Contents}, DiffWrapper) ->
357372 DiffWrapper (Content )
358373 end ,
359374 erlfmt_algebra :concat ([Fun (Elem ) || Elem <- Contents ]);
360- safe_to_algebra (Literal , _DiffWrapper ) ->
375+ safe_to_algebra ({eq , Value }, _DiffWrapper ) ->
376+ erlfmt_algebra :string (inspect (Value ));
377+ safe_to_algebra ({literal , Literal }, _DiffWrapper ) ->
361378 erlfmt_algebra :string (inspect (Literal )).
362379
363- - spec with_struct_divider (side () | {side (), side ()}, delimiter ()) -> side ().
380+ - spec with_struct_divider (block () | {supported_input (), side ()}, delimiter ()) -> side ().
364381with_struct_divider ({K , V }, Divider ) ->
365- {struct_item , K , Divider , V };
366- with_struct_divider (Side , _Divider ) ->
367- Side .
382+ {struct_item , value_to_side ( K ) , Divider , V };
383+ with_struct_divider (Block , _Divider ) ->
384+ Block .
368385
369386- spec container_to_algebra (delimiter (), [side ()], delimiter (), wrapper_fun (), fun ((side (), wrapper_fun ()) -> doc ())) ->
370387 doc ().
@@ -406,20 +423,16 @@ wrap_on_diff(Side, Fun, DiffWrapper) ->
406423extract_diff_meta ({'__block__' , [{diff , true }], [Literal ]}) -> {Literal , true };
407424extract_diff_meta (Other ) -> {Other , false }.
408425
409- - spec string_script_to_diff (script (), boolean (), [{boolean (), binary ()}], [{boolean (), binary ()}]) ->
426+ - spec string_script_to_diff (script (), [{boolean (), binary ()}], [{boolean (), binary ()}]) ->
410427 diff ().
411- string_script_to_diff ([], Equivalent , Left , Right ) ->
412- #{
413- equivalent => Equivalent ,
414- left => #{contents => lists :reverse (Left )},
415- right => #{contents => lists :reverse (Right )}
416- };
417- string_script_to_diff ([{equal , String } | Tail ], Equivalent , Left0 , Right0 ) ->
418- string_script_to_diff (Tail , Equivalent , [{false , String } | Left0 ], [{false , String } | Right0 ]);
419- string_script_to_diff ([{delete , String } | Tail ], _Equivalent , Left0 , Right0 ) ->
420- string_script_to_diff (Tail , false , [{true , String } | Left0 ], Right0 );
421- string_script_to_diff ([{insert , String } | Tail ], _Equivalent , Left0 , Right0 ) ->
422- string_script_to_diff (Tail , false , Left0 , [{true , String } | Right0 ]).
428+ string_script_to_diff ([], Left , Right ) ->
429+ {diff , #{contents => lists :reverse (Left )}, #{contents => lists :reverse (Right )}};
430+ string_script_to_diff ([{equal , String } | Tail ], Left0 , Right0 ) ->
431+ string_script_to_diff (Tail , [{false , String } | Left0 ], [{false , String } | Right0 ]);
432+ string_script_to_diff ([{delete , String } | Tail ], Left0 , Right0 ) ->
433+ string_script_to_diff (Tail , [{true , String } | Left0 ], Right0 );
434+ string_script_to_diff ([{insert , String } | Tail ], Left0 , Right0 ) ->
435+ string_script_to_diff (Tail , Left0 , [{true , String } | Right0 ]).
423436
424437- spec to_diff (side (), doc (), doc ()) -> {ok , string ()} | {error , term ()}.
425438to_diff (Side , Pre , Post ) ->
0 commit comments