Skip to content

Commit 1e081d3

Browse files
committed
Add type hovers for jkinds
1 parent 1f482cc commit 1e081d3

File tree

5 files changed

+132
-18
lines changed

5 files changed

+132
-18
lines changed

src/analysis/type_enclosing.ml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type type_info =
99
| Type of Env.t * Types.type_expr
1010
| Type_decl of Env.t * Ident.t * Types.type_declaration
1111
| Type_constr of Env.t * Types.constructor_description
12+
| Jkind of Env.t * Types.jkind_lr
1213
| String of string
1314

1415
type typed_enclosings =
@@ -34,6 +35,10 @@ let print_type ~verbosity type_info =
3435
wrap_printing_env env (fun () ->
3536
Printtyp.modtype env ppf m;
3637
Format.flush_str_formatter ())
38+
| Jkind (env, jkind) ->
39+
wrap_printing_env env (fun () ->
40+
Jkind.format_expanded ppf jkind;
41+
Format.flush_str_formatter ())
3742
| String s -> s
3843

3944
let from_nodes ~path =
@@ -57,6 +62,21 @@ let from_nodes ~path =
5762
| Module_declaration_name { md_type = { mty_type = m } }
5863
| Module_type_declaration_name { mtd_type = Some { mty_type = m } } ->
5964
ret (Modtype (env, m))
65+
| Jkind_annotation annot -> (
66+
(* CR-someday: We need to parse the annotation because the compiler doesn't include
67+
the parsed jkind in the relevant spots. We should track it so that this is less
68+
hacky. It would also make it easier to deal with with-bounds. *)
69+
(* [Jkind.of_annotation] will fail to parse jkinds with with-bounds. For now, this
70+
isn't important. Usually, users will be hovering a jkind to know what an
71+
abbreviation means. *)
72+
try
73+
(* The context isn't important. It's just used for printing error messages, which
74+
we immediately discard anyways. *)
75+
let jkind =
76+
Jkind.of_annotation ~context:(Type_variable "fake_for_merlin") annot
77+
in
78+
ret (Jkind (env, jkind))
79+
with Jkind.Error.User_error _ -> None)
6080
| Class_field
6181
{ cf_desc = Tcf_method (_, _, Tcfk_concrete (_, { exp_type })) } ->
6282
begin

src/analysis/type_enclosing.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type type_info =
3939
| Type of Env.t * Types.type_expr
4040
| Type_decl of Env.t * Ident.t * Types.type_declaration
4141
| Type_constr of Env.t * Types.constructor_description
42+
| Jkind of Env.t * Types.jkind_lr
4243
| String of string
4344

4445
type typed_enclosings =

src/ocaml/typing/jkind.ml

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,15 +1843,15 @@ module Const = struct
18431843
end
18441844

18451845
module To_out_jkind_const : sig
1846-
(** Convert a [t] into a [Outcometree.out_jkind_const].
1847-
The jkind is written in terms of the built-in jkind that requires the
1848-
least amount of modes after the mod. For example, [value mod global many
1849-
unique portable uncontended external_ non_null] could be written in
1850-
terms of [value] (as it appears above), or in terms of [immediate]
1851-
(which would just be [immediate]). Since the latter requires less modes
1852-
to be printed, it is chosen.
1846+
(** Convert a [t] into a [Outcometree.out_jkind_const]. If [expanded] is
1847+
[false], the jkind is written in terms of the built-in jkind that
1848+
requires the least amount of modes after the mod. For example,
1849+
[value mod global many unique portable uncontended external_ non_null]
1850+
could be written in terms of [value] (as it appears above), or in terms
1851+
of [immediate] (which would just be [immediate]). Since the latter
1852+
requires less modes to be printed, it is chosen.
18531853
*)
1854-
val convert : 'd t -> Outcometree.out_jkind_const
1854+
val convert : expanded:bool -> 'd t -> Outcometree.out_jkind_const
18551855
end = struct
18561856
type printable_jkind =
18571857
{ base : string;
@@ -1982,15 +1982,18 @@ module Const = struct
19821982
| [out] -> Some out
19831983
| [] -> None
19841984

1985-
let convert jkind =
1985+
let convert ~expanded jkind =
19861986
(* For each primitive jkind, we try to print the jkind in terms of it
19871987
(this is possible if the primitive is a subjkind of it). We then choose
19881988
the "simplest". The "simplest" is taken to mean the one with the least
19891989
number of modes that need to follow the [mod]. *)
19901990
let simplest =
1991-
Builtin.all
1992-
|> List.filter_map (fun base -> convert_with_base ~base jkind)
1993-
|> select_simplest
1991+
match expanded with
1992+
| false ->
1993+
Builtin.all
1994+
|> List.filter_map (fun base -> convert_with_base ~base jkind)
1995+
|> select_simplest
1996+
| true -> None
19941997
in
19951998
let printable_jkind =
19961999
match simplest with
@@ -2052,10 +2055,11 @@ module Const = struct
20522055
base with_tys
20532056
end
20542057

2055-
let to_out_jkind_const jkind = To_out_jkind_const.convert jkind
2058+
let to_out_jkind_const jkind =
2059+
To_out_jkind_const.convert ~expanded:false jkind
20562060

2057-
let format ppf jkind =
2058-
To_out_jkind_const.convert jkind |> !Oprint.out_jkind_const ppf
2061+
let format ~expanded ppf jkind =
2062+
To_out_jkind_const.convert ~expanded jkind |> !Oprint.out_jkind_const ppf
20592063

20602064
(*******************************)
20612065
(* converting user annotations *)
@@ -2181,7 +2185,7 @@ module Desc = struct
21812185
(* CR layouts v2.8: This will probably need to be overhauled with
21822186
[with]-types. See also [Printtyp.out_jkind_of_desc], which uses the same
21832187
algorithm. Internal ticket 5096. *)
2184-
let format ppf t =
2188+
let format_maybe_expanded ~expanded ppf t =
21852189
let open Format in
21862190
let rec format_desc ~nested ppf (desc : _ t) =
21872191
match desc.layout with
@@ -2194,10 +2198,12 @@ module Desc = struct
21942198
(List.map (fun layout -> { desc with layout }) lays)
21952199
| _ -> (
21962200
match get_const desc with
2197-
| Some c -> Const.format ppf c
2201+
| Some c -> Const.format ~expanded ppf c
21982202
| None -> assert false (* handled above *))
21992203
in
22002204
format_desc ppf ~nested:false t
2205+
2206+
let format ppf t = format_maybe_expanded ~expanded:false ppf t
22012207
end
22022208

22032209
module Jkind_desc = struct
@@ -3007,7 +3013,12 @@ let decompose_product ({ jkind; _ } as jk) =
30073013
doing so, because it teaches the user that e.g. [value mod local] is better
30083014
off spelled [value]. Possibly remove [jkind.annotation], but only after
30093015
we have a proper printing story. Internal ticket 5096. *)
3010-
let format ppf jkind = Desc.format ppf (Jkind_desc.get jkind.jkind)
3016+
let format_maybe_expanded ~expanded ppf jkind =
3017+
Desc.format_maybe_expanded ~expanded ppf (Jkind_desc.get jkind.jkind)
3018+
3019+
let format ppf jkind = format_maybe_expanded ~expanded:false ppf jkind
3020+
3021+
let format_expanded ppf jkind = format_maybe_expanded ~expanded:true ppf jkind
30113022

30123023
let printtyp_path = ref (fun _ _ -> assert false)
30133024

src/ocaml/typing/jkind.mli

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,10 @@ val set_raw_type_expr : (Format.formatter -> Types.type_expr -> unit) -> unit
747747

748748
val format : Format.formatter -> 'd Types.jkind -> unit
749749

750+
(** Similar to [format], but the kind is expanded as much as possible rather
751+
than written in terms of a kind abbreviation. This is used by Merlin. *)
752+
val format_expanded : Format.formatter -> 'd Types.jkind -> unit
753+
750754
(** Format the history of this jkind: what interactions it has had and why
751755
it is the jkind that it is. Might be a no-op: see [display_histories]
752756
in the implementation of the [Jkind] module.
@@ -876,3 +880,11 @@ module Debug_printers : sig
876880
val t : Format.formatter -> 'd Const.t -> unit
877881
end
878882
end
883+
884+
(* For Merlin *)
885+
886+
module Error : sig
887+
type t
888+
889+
exception User_error of Location.t * t
890+
end
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
Test that hovering over jkind annotations shows their full expansion.
2+
3+
$ file="test.ml"
4+
5+
$ print_merlin_result () {
6+
> result="$1"
7+
> line=$(echo "$result" | jq '.start.line')
8+
> start=$(echo "$result" | jq '.start.col')
9+
> end=$(echo "$result" | jq '.end.col')
10+
>
11+
> start_for_cut=$((start + 1))
12+
> end_for_cut=$((end + 1))
13+
> value=$(sed -n "${line}p" "$file" | cut -c "${start_for_cut}-${end_for_cut}")
14+
> type=$(echo "$result" | jq '.type' -r)
15+
> echo "\"$value\" : \"$type\""
16+
> }
17+
18+
$ hover () {
19+
> line="$1"
20+
> col="$2"
21+
> enclosings="$3"
22+
>
23+
> # Print the location we are hovering
24+
> sed -n "${line}p" "$file"
25+
> printf '%*s^\n' "$col" ''
26+
>
27+
> # Then print the output from Merlin
28+
> $MERLIN single type-enclosing -position "$line:$col" -filename "$file" < "$file" \
29+
> | jq -c ".value[:$enclosings][]" \
30+
> | while read -r result; do
31+
> print_merlin_result "$result"
32+
> done
33+
> }
34+
35+
$ cat > "$file" << EOF
36+
> type t1 : immutable_data
37+
> type t2 : value mod portable
38+
> type ('a : immediate) t3 : value
39+
> type 'a t4 : immutable_data mod global with 'a
40+
> EOF
41+
42+
$ hover 1 14 1
43+
type t1 : immutable_data
44+
^
45+
"immutable_data" : "value mod many forkable unyielding stateless immutable non_float"
46+
47+
$ hover 2 11 2
48+
type t2 : value mod portable
49+
^
50+
"value " : "value"
51+
"value mod portable" : "value mod portable"
52+
53+
$ hover 3 16 1
54+
type ('a : immediate) t3 : value
55+
^
56+
"immediate)" : "value mod global aliased many stateless immutable external_ non_float"
57+
58+
$ hover 3 28 2
59+
type ('a : immediate) t3 : value
60+
^
61+
"value" : "value"
62+
"type ('a : immediate) t3 : value" : "type ('a : immediate) t3"
63+
64+
# CR-someday: It'd be nice to print the with-bounds when we enclose the whole jkind
65+
$ hover 4 20 3
66+
type 'a t4 : immutable_data mod global with 'a
67+
^
68+
"immutable_data " : "value mod many forkable unyielding stateless immutable non_float"
69+
"immutable_data mod global " : "value mod global many stateless immutable non_float"
70+
"type 'a t4 : immutable_data mod global with 'a" : "type 'a t4 : immutable_data mod global yielding unforkable with 'a"

0 commit comments

Comments
 (0)