Source file hover.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
open Names
open Printer
open Pp
let md_code_block s =
Format.sprintf "```coq\n%s\n```" s
let compactify s =
let replacements = [
Str.regexp {|\bfun\b|}, "λ";
Str.regexp {|\bforall\b|}, "∀";
Str.regexp {|\bexists\b|}, "∃";
Str.regexp {| \\/ |}, " ∨ ";
Str.regexp {| /\\ |}, " ∧ ";
Str.regexp {| <-> |}, " ↔ ";
Str.regexp {| -> |}, " → ";
Str.regexp {| <= )|}, " ≤ ";
Str.regexp {| >= )|}, " ≥ ";
Str.regexp {| => )|}, " ⇒ ";
Str.regexp {| <> )|}, " ≠ ";
Str.regexp {| ~ )|}, " ¬ "
]
in
List.fold_left (fun s (reg,repl) -> Str.global_replace reg repl s) s replacements
[%%if coq = "8.18"]
let pr_s = prlist (fun CAst.{v=s} -> str "%" ++ str s)
let eq_realarg = List.equal (fun a b -> String.equal a.CAst.v b.CAst.v)
let nargs_maximal_of_pos ((na,_,_),_,(maximal,_)) = na, maximal
let make_scope = CAst.make
[%%else]
let pr_delimiter_depth = function
| Constrexpr.DelimOnlyTmpScope -> str "%_"
| Constrexpr.DelimUnboundedScope -> str "%"
let pr_scope_delimiter (d, sc) = pr_delimiter_depth d ++ str sc
let pr_s = prlist (fun CAst.{v=s} -> pr_scope_delimiter s)
let eq_realarg =
List.equal
(fun a b -> let da, a = a.CAst.v in let db, b = b.CAst.v in
da = db && String.equal a b)
let nargs_maximal_of_pos imp =
let (na, _, _) = imp.Impargs.impl_pos in
na, imp.Impargs.impl_max
let make_scope = (fun s -> CAst.make (Constrexpr.DelimUnboundedScope, s))
[%%endif]
let pr_args args more_implicits mods =
let open Vernacexpr in
let pr_if b x = if b then x else str "" in
let pr_one_arg (x,k) = pr_if k (str"!") ++ Name.print x in
let pr_br imp force x =
let left,right =
match imp with
| Glob_term.NonMaxImplicit -> str "[", str "]"
| Glob_term.MaxImplicit -> str "{", str "}"
| Glob_term.Explicit -> if force then str"(",str")" else mt(),mt()
in
left ++ x ++ right
in
let get_arguments_like s imp tl =
if s = [] && imp = Glob_term.Explicit then [], tl
else
let rec fold = function
| RealArg arg :: tl when eq_realarg arg.notation_scope s
&& arg.implicit_status = imp ->
fold ((arg.name,arg.recarg_like) :: extra) tl
| args -> List.rev extra, args
in
fold [] tl
in
let rec print_arguments = function
| [] -> mt()
| VolatileArg :: l -> spc () ++ str"/" ++ print_arguments l
| BidiArg :: l -> spc () ++ str"&" ++ print_arguments l
| RealArg { name = id; recarg_like = k;
notation_scope = s;
implicit_status = imp } :: tl ->
let , tl = get_arguments_like s imp tl in
spc() ++ hov 1 (pr_br imp (extra<>[]) (prlist_with_sep spc pr_one_arg ((id,k)::extra)) ++
pr_s s) ++ print_arguments tl
in
let rec print_implicits = function
| [] -> mt ()
| (name, impl) :: rest ->
spc() ++ pr_br impl false (Name.print name) ++ print_implicits rest
in
print_arguments args ++
if not (CList.is_empty more_implicits) then
prlist (fun l -> str"," ++ print_implicits l) more_implicits
else (mt ()) ++
(if not (CList.is_empty mods) then str" : " else str"") ++
prlist_with_sep (fun () -> str", " ++ spc()) (function
| `ReductionDontExposeCase -> str "simpl nomatch"
| `ReductionNeverUnfold -> str "simpl never"
| `DefaultImplicits -> str "default implicits"
| `Rename -> str "rename"
| `Assert -> str "assert"
| `ExtraScopes -> str "extra scopes"
| `ClearImplicits -> str "clear implicits"
| `ClearScopes -> str "clear scopes"
| `ClearBidiHint -> str "clear bidirectionality hint")
mods
let implicit_kind_of_status = function
| None -> Anonymous, Glob_term.Explicit
| Some imp ->
let na, maximal = nargs_maximal_of_pos imp in
na, if maximal then Glob_term.MaxImplicit else Glob_term.NonMaxImplicit
let imp =
let _,imp = implicit_kind_of_status imp in
(Anonymous, imp)
let ref scopes =
let open Constr in
let rec aux env t = function
| [] -> false
| _::scopes -> match kind (Reduction.whd_all env t) with
| Prod (na,dom,codom) -> aux (Environ.push_rel (Context.Rel.Declaration.LocalAssum (na,dom)) env) codom scopes
| _ -> true
in
let env = Global.env() in
let ty, _ctx = Typeops.type_of_global_in_context env ref in
aux env ty scopes
let rec main_implicits i renames recargs scopes impls =
if renames = [] && recargs = [] && scopes = [] && impls = [] then []
else
let recarg_like, recargs = match recargs with
| j :: recargs when i = j -> true, recargs
| _ -> false, recargs
in
let (name, implicit_status) =
match renames, impls with
| _, (Some _ as i) :: _ -> implicit_kind_of_status i
| name::_, _ -> (name,Glob_term.Explicit)
| [], (None::_ | []) -> (Anonymous, Glob_term.Explicit)
in
let notation_scope = match scopes with
| scope :: _ -> List.map make_scope scope
| [] -> []
in
let status = {Vernacexpr.implicit_status; name; recarg_like; notation_scope} in
let tl = function [] -> [] | _::tl -> tl in
let rest = main_implicits (i+1) (tl renames) recargs (tl scopes) (tl impls) in
status :: rest
let dummy = {
Vernacexpr.implicit_status = Glob_term.Explicit;
name = Anonymous;
recarg_like = false;
notation_scope = [];
}
let is_dummy = function
| Vernacexpr.(RealArg {implicit_status; name; recarg_like; notation_scope}) ->
name = Anonymous && not recarg_like && notation_scope = [] && implicit_status = Glob_term.Explicit
| _ -> false
let rec insert_fake_args volatile bidi impls =
let open Vernacexpr in
match volatile, bidi with
| Some 0, _ -> VolatileArg :: insert_fake_args None bidi impls
| _, Some 0 -> BidiArg :: insert_fake_args volatile None impls
| None, None -> List.map (fun a -> RealArg a) impls
| _, _ ->
let hd, tl = match impls with
| impl :: impls -> impl, impls
| [] -> dummy, []
in
let f = Option.map pred in
RealArg hd :: insert_fake_args (f volatile) (f bidi) tl
[%%if coq = "8.18"]
let ref_of_const x = Some x
[%%else]
let ref_of_const = function
| GlobRef.ConstRef ref -> Some ref
| _ -> None
[%%endif]
let print_arguments ref =
let flags, recargs, nargs_for_red =
let open Reductionops.ReductionBehaviour in
match ref_of_const ref with
| Some ref ->
begin match get ref with
| None -> [], [], None
| Some NeverUnfold -> [`ReductionNeverUnfold], [], None
| Some (UnfoldWhen { nargs; recargs }) -> [], recargs, nargs
| Some (UnfoldWhenNoMatch { nargs; recargs }) -> [`ReductionDontExposeCase], recargs, nargs
end
| None -> [], [], None
in
let names, not_renamed =
try Arguments_renaming.arguments_names ref, false
with Not_found ->
let env = Global.env () in
let ty, _ = Typeops.type_of_global_in_context env ref in
List.map Util.pi1 (Impargs.compute_implicits_names env (Evd.from_env env) (EConstr.of_constr ty)), true in
let scopes = Notation.find_arguments_scope ref in
let flags = if needs_extra_scopes ref scopes then `ExtraScopes::flags else flags in
let impls = Impargs.extract_impargs_data (Impargs.implicits_of_global ref) in
let impls, moreimpls = match impls with
| (_, impls) :: rest -> impls, rest
| [] -> assert false
in
let impls = main_implicits 0 names recargs scopes impls in
let moreimpls = List.map (fun (_,i) -> List.map extra_implicit_kind_of_status i) moreimpls in
let bidi = Pretyping.get_bidirectionality_hint ref in
let impls = insert_fake_args nargs_for_red bidi impls in
if List.for_all is_dummy impls && moreimpls = [] && flags = [] then mt ()
else
pr_args impls moreimpls flags ++
(if not_renamed then mt () else
fnl () ++ str " (where some original arguments have been renamed)")
let canonical_name_info ref =
let cref = Globnames.canonical_gr ref in
if GlobRef.UserOrd.equal ref cref then mt ()
else match Nametab.path_of_global cref with
| path -> spc() ++ str "(syntactically equal to" ++ spc() ++ Libnames.pr_path path ++ str ")"
| exception Not_found -> spc() ++ str "(missing canonical, bug?)"
let pr_globref_kind = let open GlobRef in function
| ConstRef _ -> "Constant"
| IndRef _ -> "Inductive"
| ConstructRef _ -> "Constructor"
| VarRef _ -> "Variable"
let ref_info env _sigma ref udecl =
let typ, _univs = Typeops.type_of_global_in_context env ref in
let bl = Printer.universe_binders_with_opt_names (Environ.universes_of_global env ref) udecl in
let sigma = Evd.from_ctx (UState.of_names bl) in
let typ = Arguments_renaming.rename_type typ ref in
let impargs = Impargs.select_stronger_impargs (Impargs.implicits_of_global ref) in
let impargs = List.map Impargs.binding_kind_of_status impargs in
let typ = pr_ltype_env env sigma ~impargs typ in
let path = Libnames.pr_path (Nametab.path_of_global ref) ++ canonical_name_info ref in
let args = print_arguments ref in
let args = if Pp.ismt args then [] else ["**Args:** " ^ (Pp.string_of_ppcmds (print_arguments ref))] in
String.concat "\n\n---\n\n" @@ [md_code_block @@ compactify @@ Pp.string_of_ppcmds typ]@args@
[Format.sprintf "**%s** %s" (pr_globref_kind ref) (Pp.string_of_ppcmds path)]
let mod_info mp =
md_code_block @@ Format.sprintf "Module %s" (DirPath.to_string (Nametab.dirpath_of_module mp))
let modtype_info mp =
md_code_block @@ Format.sprintf "Module Type %s" (Libnames.string_of_path (Nametab.path_of_modtype mp))
let syndef_info kn =
md_code_block @@ Format.sprintf "Notation %s" (Libnames.string_of_path (Nametab.path_of_abbreviation kn))
let get_hover_contents env sigma ref_or_by_not =
match ref_or_by_not.CAst.v with
| Constrexpr.AN qid ->
let r = begin try
let udecl = None in
let ref = Nametab.locate qid in
Some (ref_info env sigma ref udecl)
with Not_found ->
try
let kn = Nametab.locate_abbreviation qid in
Some (syndef_info kn)
with Not_found ->
try
let mp = Nametab.locate_module qid in
Some (mod_info mp)
with Not_found ->
try
let mp = Nametab.locate_modtype qid in
Some (modtype_info mp)
with Not_found ->
None
end
in
Option.map Lsp.Types.(fun value -> MarkupContent.create ~kind:MarkupKind.Markdown ~value) r
| Constrexpr.ByNotation (_ntn,_sc) -> assert false