Skip to content

Commit f296974

Browse files
authored
Improve diff representation for record types (#109)
* Add cram tests for record type modifications * Add a simple diff representation for record types * Update record diff representation * Print the new record diff representation * Update record diff rep. * Minor fixes * Fix printing of modified record types * Change the diff representation for modified record types to be more minimal * Fix checking of label declarations type_expr * Minor fix * Add changelog entry
1 parent aa854ba commit f296974

File tree

6 files changed

+259
-57
lines changed

6 files changed

+259
-57
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
### Changed
1212

13+
- Improve diff representation of modified record types (#109, @azzsal)
14+
1315
### Deprecated
1416

1517
### Fixed

lib/diff.ml

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ type value = {
1212
vdiff : (value_description, value_description atomic_modification) t;
1313
}
1414

15-
type type_ = {
16-
tname : string;
17-
tdiff : (type_declaration, type_declaration atomic_modification) t;
15+
type type_modification =
16+
| Compound of record_field list
17+
| Atomic of type_declaration atomic_modification
18+
19+
and record_field = {
20+
lname : string;
21+
ldiff : (label_declaration, label_declaration atomic_modification) t;
1822
}
1923

24+
type type_ = { tname : string; tdiff : (type_declaration, type_modification) t }
2025
type class_modification = Unsupported
2126

2227
type class_ = {
@@ -95,7 +100,12 @@ let module_type_fallback ~loc ~typing_env ~name ~reference ~current =
95100
| exception Includemod.Error _ ->
96101
Some (Module { mname = name; mdiff = Modified Unsupported })
97102

98-
let type_item ~typing_env ~name ~reference ~current =
103+
let extract_lbls lbls =
104+
List.fold_left
105+
(fun map lbl -> String_map.add (Ident.name lbl.ld_id) lbl map)
106+
String_map.empty lbls
107+
108+
let rec type_item ~typing_env ~name ~reference ~current =
99109
match (reference, current) with
100110
| None, None -> None
101111
| Some (reference, _), None ->
@@ -113,8 +123,44 @@ let type_item ~typing_env ~name ~reference ~current =
113123
in
114124
match (type_coercion1 (), type_coercion2 ()) with
115125
| None, None -> None
116-
| _, _ ->
117-
Some (Type { tname = name; tdiff = Modified { reference; current } }))
126+
| _, _ -> (
127+
match (reference.type_kind, current.type_kind) with
128+
| Type_record (ref_label_lst, _), Type_record (cur_label_lst, _) ->
129+
let changed_lbls =
130+
modified_record_type ~typing_env ~ref_label_lst ~cur_label_lst
131+
in
132+
Some
133+
(Type { tname = name; tdiff = Modified (Compound changed_lbls) })
134+
| _, _ ->
135+
Some
136+
(Type
137+
{
138+
tname = name;
139+
tdiff = Modified (Atomic { reference; current });
140+
})))
141+
142+
and modified_record_type ~typing_env ~ref_label_lst ~cur_label_lst =
143+
let ref_lbls = extract_lbls ref_label_lst in
144+
let curr_lbls = extract_lbls cur_label_lst in
145+
let changed_lbls =
146+
String_map.merge
147+
(fun name ref cur ->
148+
match (ref, cur) with
149+
| None, None -> None
150+
| Some ref, None -> Some { lname = name; ldiff = Removed ref }
151+
| None, Some cur -> Some { lname = name; ldiff = Added cur }
152+
| Some ref, Some cur ->
153+
if Ctype.does_match typing_env ref.ld_type cur.ld_type then None
154+
else
155+
Some
156+
{
157+
lname = name;
158+
ldiff = Modified { reference = ref; current = cur };
159+
})
160+
ref_lbls curr_lbls
161+
|> String_map.bindings |> List.map snd
162+
in
163+
changed_lbls
118164

119165
let value_item ~typing_env ~name ~reference ~current =
120166
match (reference, current) with

lib/diff.mli

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,19 @@ and cltype = {
2121
ctdiff : (Types.class_type_declaration, class_type_modification) t;
2222
}
2323

24+
type type_modification =
25+
| Compound of record_field list
26+
| Atomic of Types.type_declaration atomic_modification
27+
28+
and record_field = {
29+
lname : string;
30+
ldiff :
31+
(Types.label_declaration, Types.label_declaration atomic_modification) t;
32+
}
33+
2434
type type_ = {
2535
tname : string;
26-
tdiff : (Types.type_declaration, Types.type_declaration atomic_modification) t;
36+
tdiff : (Types.type_declaration, type_modification) t;
2737
}
2838

2939
type module_ = {

lib/text_diff.ml

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
open Types
2-
31
type conflict2 = { orig : string list; new_ : string list }
4-
type t = conflict2 list String_map.t
5-
type printer = { same : string Fmt.t; diff : conflict2 Fmt.t }
2+
type hunk = Change of conflict2 | Same of string
3+
type t = hunk list String_map.t
4+
type printer = { same : string Fmt.t; diff : hunk Fmt.t }
65

76
let printer ~same ~diff = { same; diff }
87

8+
let git_diff_printer ppf c =
9+
match c with
10+
| Change { orig; new_ } ->
11+
List.iter (fun line -> Fmt.pf ppf "-%s\n" line) orig;
12+
List.iter (fun line -> Fmt.pf ppf "+%s\n" line) new_
13+
| Same common -> Fmt.pf ppf "%s\n" common
14+
915
let git_printer =
10-
{
11-
same = (fun ppf -> Fmt.pf ppf " %s\n");
12-
diff =
13-
(fun ppf { orig; new_ } ->
14-
List.iter (fun line -> Fmt.pf ppf "-%s\n" line) orig;
15-
List.iter (fun line -> Fmt.pf ppf "+%s\n" line) new_);
16-
}
16+
{ same = (fun ppf -> Fmt.pf ppf " %s\n"); diff = git_diff_printer }
1717

1818
let pp_ diff_printer =
1919
let pp_dh ppf dh = match dh with c -> diff_printer.diff ppf c in
@@ -26,23 +26,30 @@ let vd_to_lines name vd =
2626
Format.pp_print_flush formatter ();
2727
CCString.lines (Buffer.contents buf)
2828

29-
let td_to_lines name vd =
29+
let td_to_lines name td =
30+
let buf = Buffer.create 256 in
31+
let formatter = Format.formatter_of_buffer buf in
32+
Printtyp.type_declaration (Ident.create_local name) formatter td;
33+
Format.pp_print_flush formatter ();
34+
CCString.lines (Buffer.contents buf)
35+
36+
let lbl_to_lines ld =
3037
let buf = Buffer.create 256 in
3138
let formatter = Format.formatter_of_buffer buf in
32-
Printtyp.type_declaration (Ident.create_local name) formatter vd;
39+
Printtyp.label formatter ld;
3340
Format.pp_print_flush formatter ();
3441
CCString.lines (Buffer.contents buf)
3542

3643
let md_to_lines name md =
3744
let buf = Buffer.create 256 in
3845
let formatter = Format.formatter_of_buffer buf in
39-
Printtyp.modtype formatter md.md_type;
46+
Printtyp.modtype formatter Types.(md.md_type);
4047
Format.pp_print_flush formatter ();
4148
let module_str = "module " ^ name ^ ": " ^ Buffer.contents buf in
4249
CCString.lines module_str
4350

4451
let mtd_to_lines name mtd =
45-
match mtd.mtd_type with
52+
match Types.(mtd.mtd_type) with
4653
| Some m ->
4754
let buf = Buffer.create 256 in
4855
let formatter = Format.formatter_of_buffer buf in
@@ -72,50 +79,96 @@ let ctd_to_lines name cd =
7279
let class_str = Buffer.contents buf in
7380
CCString.lines class_str
7481

75-
let process_diff (diff : (_, _ Diff.atomic_modification) Diff.t) name to_lines =
82+
let process_atomic_diff (diff : (_, _ Diff.atomic_modification) Diff.t) name
83+
to_lines =
7684
match diff with
77-
| Added item -> [ { orig = []; new_ = to_lines name item } ]
78-
| Removed item -> [ { orig = to_lines name item; new_ = [] } ]
85+
| Added item -> [ Change { orig = []; new_ = to_lines name item } ]
86+
| Removed item -> [ Change { orig = to_lines name item; new_ = [] } ]
7987
| Modified { reference; current } ->
80-
[ { orig = to_lines name reference; new_ = to_lines name current } ]
88+
[
89+
Change { orig = to_lines name reference; new_ = to_lines name current };
90+
]
8191

8292
let process_value_diff (val_diff : Diff.value) =
83-
process_diff val_diff.vdiff val_diff.vname vd_to_lines
93+
process_atomic_diff val_diff.vdiff val_diff.vname vd_to_lines
94+
95+
let process_lbl_diff
96+
(diff :
97+
( Types.label_declaration,
98+
Types.label_declaration Diff.atomic_modification )
99+
Diff.t) =
100+
match diff with
101+
| Added item -> [ Change { orig = []; new_ = lbl_to_lines item } ]
102+
| Removed item -> [ Change { orig = lbl_to_lines item; new_ = [] } ]
103+
| Modified { reference; current } ->
104+
[ Change { orig = lbl_to_lines reference; new_ = lbl_to_lines current } ]
105+
106+
let rec process_type_diff (type_diff : Diff.type_) =
107+
match type_diff.tdiff with
108+
| Diff.Modified (Compound change_lst) ->
109+
process_modified_record_type_diff type_diff.tname change_lst
110+
| Diff.Modified (Atomic mods) ->
111+
process_atomic_diff (Diff.Modified mods) type_diff.tname td_to_lines
112+
| Diff.Added td ->
113+
process_atomic_diff (Diff.Added td) type_diff.tname td_to_lines
114+
| Diff.Removed td ->
115+
process_atomic_diff (Diff.Removed td) type_diff.tname td_to_lines
116+
117+
and process_modified_record_type_diff name diff =
118+
let indent_lbl c =
119+
match c with
120+
| Same c -> Same (" " ^ c)
121+
| Change { orig; new_ } ->
122+
Change
123+
{
124+
orig = List.map (fun s -> " " ^ s) orig;
125+
new_ = List.map (fun s -> " " ^ s) new_;
126+
}
127+
in
128+
let changes = process_changed_labels diff in
129+
[ Same ("type " ^ name ^ " = {") ]
130+
@ [ indent_lbl (Same "...") ]
131+
@ List.map (fun c -> indent_lbl c) changes
132+
@ [ Same "}" ]
84133

85-
let process_type_diff (type_diff : Diff.type_) =
86-
process_diff type_diff.tdiff type_diff.tname td_to_lines
134+
and process_changed_labels (lbls_diffs : Diff.record_field list) =
135+
List.flatten
136+
(List.map
137+
(fun (lbl_diff : Diff.record_field) -> process_lbl_diff lbl_diff.ldiff)
138+
lbls_diffs)
87139

88140
let process_class_diff (class_diff : Diff.class_) =
89141
match class_diff.cdiff with
90-
| Added cd -> [ { orig = []; new_ = cd_to_lines class_diff.cname cd } ]
91-
| Removed cd -> [ { orig = cd_to_lines class_diff.cname cd; new_ = [] } ]
142+
| Added cd -> [ Change { orig = []; new_ = cd_to_lines class_diff.cname cd } ]
143+
| Removed cd ->
144+
[ Change { orig = cd_to_lines class_diff.cname cd; new_ = [] } ]
92145
| Modified _ -> []
93146

94147
let process_class_type_diff (class_type_diff : Diff.cltype) =
95148
match class_type_diff.ctdiff with
96149
| Added ctd ->
97-
[ { orig = []; new_ = ctd_to_lines class_type_diff.ctname ctd } ]
150+
[ Change { orig = []; new_ = ctd_to_lines class_type_diff.ctname ctd } ]
98151
| Removed ctd ->
99-
[ { orig = ctd_to_lines class_type_diff.ctname ctd; new_ = [] } ]
152+
[ Change { orig = ctd_to_lines class_type_diff.ctname ctd; new_ = [] } ]
100153
| Modified _ -> []
101154

102155
let rec process_sig_diff :
103156
type a. _ -> (string -> a -> string list) -> (a, _) Diff.t * _ -> _ -> _ =
104157
fun path to_lines ((diff : (a, _) Diff.t), name) acc ->
105158
match diff with
106159
| Added curr_mtd ->
107-
let diff = [ { orig = []; new_ = to_lines name curr_mtd } ] in
160+
let diff = [ Change { orig = []; new_ = to_lines name curr_mtd } ] in
108161
String_map.update path
109162
(function None -> Some diff | Some existing -> Some (existing @ diff))
110163
acc
111164
| Removed ref_mtd ->
112-
let diff = [ { orig = to_lines name ref_mtd; new_ = [] } ] in
165+
let diff = [ Change { orig = to_lines name ref_mtd; new_ = [] } ] in
113166
String_map.update path
114167
(function None -> Some diff | Some existing -> Some (existing @ diff))
115168
acc
116169
| Modified Diff.Unsupported ->
117170
String_map.add path
118-
[ { orig = []; new_ = [ "<unsupported change>" ] } ]
171+
[ Change { orig = []; new_ = [ "<unsupported change>" ] } ]
119172
acc
120173
| Modified (Supported changes) -> signature_changes path changes acc
121174

@@ -200,9 +253,12 @@ module With_colors = struct
200253
let pp_keep fmt line = Format.fprintf fmt " %s\n" line
201254

202255
let printer =
203-
printer ~same:pp_keep ~diff:(fun fmt { orig; new_ } ->
204-
List.iter (pp_remove fmt) orig;
205-
List.iter (pp_add fmt) new_)
256+
printer ~same:pp_keep ~diff:(fun fmt c ->
257+
match c with
258+
| Change { orig; new_ } ->
259+
List.iter (pp_remove fmt) orig;
260+
List.iter (pp_add fmt) new_
261+
| Same common -> (pp_keep fmt) common)
206262

207263
let pp_diff fmt diff = pp_ printer fmt diff
208264
let pp fmt t = gen_pp pp_diff fmt t

lib/text_diff.mli

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
(** Utilities for custom diff printing *)
1+
(** Utilities for custom diff printing *)
22

3-
type conflict2 = { orig : string list; new_ : string list }
4-
5-
type t = conflict2 list String_map.t
3+
type t
64
(** Type for representing library interface diffs as text diff.
75
8-
Changes are arranged per fully qualified module path.
9-
Keys are module path, as strings, that map to the textual diff
10-
for the content of said module.
11-
12-
The removal or addition of a module is listed under its parent.
13-
E.g. if [Main.M] was removed, this will show in the textual diff
14-
under the key ["Main"].
15-
On the other hand, if [Main.M] is present in both versions but received
16-
a new function [Main.M.do_something], this will show in the textual
17-
diff under the key ["Main.M"].
18-
Identical modules won't appear in the map.
19-
*)
6+
Changes are arranged per fully qualified module path. Keys are module path,
7+
as strings, that map to the textual diff for the content of said module.
8+
9+
The removal or addition of a module is listed under its parent. E.g. if
10+
[Main.M] was removed, this will show in the textual diff under the key
11+
["Main"]. On the other hand, if [Main.M] is present in both versions but
12+
received a new function [Main.M.do_something], this will show in the textual
13+
diff under the key ["Main.M"]. Identical modules won't appear in the map. *)
2014

2115
val pp : Format.formatter -> t -> unit
2216
(** Pretty-print the text diff in a human readable, git diff like format. *)
@@ -26,6 +20,6 @@ val from_diff : Diff.module_ -> t
2620

2721
module With_colors : sig
2822
val pp : Format.formatter -> t -> unit
29-
(** Same as regular [pp] but prints added lines in green and removed lines
30-
in red. *)
23+
(** Same as regular [pp] but prints added lines in green and removed lines in
24+
red. *)
3125
end

0 commit comments

Comments
 (0)