Skip to content

Commit 4f28f92

Browse files
committed
Merge branch 'v8.18' into v8.17
2 parents ce72a62 + 6c5fd73 commit 4f28f92

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+852
-228
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ jobs:
114114
- name: 🐫🐪🐫 Get dependencies
115115
run: |
116116
opam exec -- make opam-deps
117-
opam pin add js_of_ocaml-compiler https://github.com/ejgallego/js_of_ocaml.git#fix_build_fs_target -y
118-
opam pin add js_of_ocaml https://github.com/ejgallego/js_of_ocaml.git#fix_build_fs_target -y
117+
opam pin add js_of_ocaml-compiler --dev -y
118+
opam pin add js_of_ocaml --dev -y
119119
opam install zarith_stubs_js js_of_ocaml-ppx -y
120120
121121
- name: 💉💉💉 Patch Coq

CHANGES.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
# unreleased
2-
------------
1+
# coq-lsp 0.2.2: To Virtual or not To Virtual
2+
---------------------------------------------
3+
4+
- [vscode] Expand selectors to include `vscode-vfs://` URIs used in
5+
`github.dev`, document limited virtual workspace support in
6+
`package.json` (@ejgallego, #849)
7+
8+
# coq-lsp 0.2.1: Click !
9+
------------------------
310

411
- [deps] Bump toolchain so minimal `ppxlib` is 0.26, in order to fix
512
some `ppx_import` oddities. This means our lower bound for the Jane
@@ -34,6 +41,18 @@
3441
info that is needed is at hand. It could also be tried to set the
3542
build target for immediate requests to the view hint, but we should
3643
see some motivation for that (@ejgallego, #841)
44+
- [lsp] [diagnostics] (! breaking change) change type of diagnostics
45+
extra data from list to named record (@ejgallego, #843)
46+
- [lsp] Implement support for `textDocument/codeAction`. For now, we
47+
support Coq's 8.21 `quickFix` data (@ejgallego, #840, #843, #845)
48+
- [petanque] Fix bug for detection of proof finished in deep stacks
49+
(@ejgallego, @gbdrt, #847)
50+
- [goals request] allow multiple Coq sentences in `command`. This is
51+
backwards compatible in the case that commands do not error, and
52+
users were sending just one command. (@ejgallego, #823, thanks to
53+
CoqPilot devs and G. Baudart for feedback)
54+
- [goals request] (! breaking) fail the request if the commands in
55+
`command` fail (@ejgallego, #823)
3756

3857
# coq-lsp 0.2.0: From Green to Blue
3958
-----------------------------------

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ The checklist for the release as of today is the following:
411411
412412
The above can be done with:
413413
```
414-
export COQLSPV=0.2.0
414+
export COQLSPV=0.2.1
415415
git checkout main && make && dune-release tag ${COQLSPV}
416416
git checkout v8.20 && git merge main && make && dune-release tag ${COQLSPV}+8.20 && dune-release
417417
git checkout v8.19 && git merge v8.20 && make && dune-release tag ${COQLSPV}+8.19 && dune-release

Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ _LIBROOT=$(shell opam var lib)
159159
ifdef VENDORED_SETUP
160160
_CCROOT=_build/install/default/lib/coq-core
161161
else
162+
# We could use `opam var lib` as well here, as the idea to rely on
163+
# coqc was to avoid having a VENDORED_SETUP variable, which we now
164+
# have anyways.
162165
_CCROOT=$(shell coqc -where)/../coq-core
163166
endif
164167

@@ -169,8 +172,8 @@ controller-js/coq-fs-core.js: coq_boot
169172
for i in $$(find $(_CCROOT)/plugins -name *.cma); do js_of_ocaml --dynlink $$i; done
170173
for i in $$(find _build/install/default/lib/coq-lsp/serlib -wholename */*.cma); do js_of_ocaml --dynlink $$i; done
171174
js_of_ocaml build-fs -o controller-js/coq-fs-core.js \
172-
$$(find $(_CCROOT)/ \( -wholename '*/plugins/*/*.js' -or -wholename '*/META' \) -printf "%p:/static/lib/%p ") \
173-
$$(find _build/install/default/lib/coq-lsp/ \( -wholename '*/serlib/*/*.js' -or -wholename '*/META' \) -printf "%p:/static/lib/%p ") \
175+
$$(find $(_CCROOT)/ \( -wholename '*/plugins/*/*.js' -or -wholename '*/META' \) -printf "%p:/static/lib/coq-core/%P ") \
176+
$$(find _build/install/default/lib/coq-lsp/ \( -wholename '*/serlib/*/*.js' -or -wholename '*/META' \) -printf "%p:/static/lib/coq-lsp/%P ") \
174177
./etc/META.threads:/static/lib/threads/META \
175178
$$(find $(_LIBROOT) -wholename '*/str/META' -printf "%p:/static/lib/%P ") \
176179
$$(find $(_LIBROOT) -wholename '*/seq/META' -printf "%p:/static/lib/%P ") \
@@ -197,6 +200,11 @@ controller-js/coq-fs-core.js: coq_boot
197200
# These libs are actually linked, so no cma is needed.
198201
# $$(find $(_LIBROOT) -wholename '*/zarith/*.cma' -printf "%p:/static/lib/%P " -or -wholename '*/zarith/META' -printf "%p:/static/lib/%P ")
199202

203+
.PHONY: check-js-fs-sanity
204+
check-js-fs-sanity: controller-js/coq-fs-core.js js
205+
cat _build/default/controller-js/coq-fs.js | grep '/static/'
206+
cat controller-js/coq-fs-core.js | grep '/static/'
207+
200208
# Serlib plugins require:
201209
# ppx_compare.runtime-lib
202210
# ppx_deriving.runtime

controller-js/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ extension + Coq build.
6262
As of Feb 2023, due to security restrictions, you may need to either:
6363

6464
- pass `--enable-coi` to `code`
65-
- use ``?enable-coi=` in the vscode dev setup
65+
- append `?vscode-coi` in the vscode dev setup URL
6666

6767
in order to have interruptions (`SharedBufferArray`) working.
6868

controller-js/coq_lsp_worker.ml

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,6 @@ module LSP = Lsp.Base
1414
open Js_of_ocaml
1515
open Controller
1616

17-
let rec obj_to_json (cobj : < .. > Js.t) : Yojson.Safe.t =
18-
let open Js in
19-
let open Js.Unsafe in
20-
let typeof_cobj = to_string (typeof cobj) in
21-
match typeof_cobj with
22-
| "string" -> `String (to_string @@ coerce cobj)
23-
| "boolean" -> `Bool (to_bool @@ coerce cobj)
24-
| "number" -> `Int (int_of_float @@ float_of_number @@ coerce cobj)
25-
| _ ->
26-
if instanceof cobj array_empty then
27-
`List Array.(to_list @@ map obj_to_json @@ to_array @@ coerce cobj)
28-
else if instanceof cobj Typed_array.arrayBuffer then
29-
`String (Typed_array.String.of_arrayBuffer @@ coerce cobj)
30-
else if instanceof cobj Typed_array.uint8Array then
31-
`String (Typed_array.String.of_uint8Array @@ coerce cobj)
32-
else
33-
let json_string = Js.to_string (Json.output cobj) in
34-
Yojson.Safe.from_string json_string
35-
36-
let rec json_to_obj (cobj : < .. > Js.t) (json : Yojson.Safe.t) : < .. > Js.t =
37-
let open Js.Unsafe in
38-
let ofresh j = json_to_obj (obj [||]) j in
39-
match json with
40-
| `Bool b -> coerce @@ Js.bool b
41-
| `Null -> pure_js_expr "null"
42-
| `Assoc l ->
43-
List.iter (fun (p, js) -> set cobj p (ofresh js)) l;
44-
cobj
45-
| `List l -> Array.(Js.array @@ map ofresh (of_list l))
46-
| `Float f -> coerce @@ Js.number_of_float f
47-
| `String s -> coerce @@ Js.string s
48-
| `Int m -> coerce @@ Js.number_of_float (float_of_int m)
49-
| `Intlit s -> coerce @@ Js.number_of_float (float_of_string s)
50-
| `Tuple t -> Array.(Js.array @@ map ofresh (of_list t))
51-
| `Variant (_, _) -> pure_js_expr "undefined"
52-
5317
let findlib_conf = "\ndestdir=\"/static/lib\"path=\"/static/lib\""
5418
let findlib_path = "/static/lib/findlib.conf"
5519

@@ -65,7 +29,7 @@ let setup_std_printers () =
6529

6630
let post_message (msg : Lsp.Base.Message.t) =
6731
let json = Lsp.Base.Message.to_yojson msg in
68-
let js = json_to_obj (Js.Unsafe.obj [||]) json in
32+
let js = Jsso.json_to_obj json in
6933
Worker.post_message js
7034

7135
type opaque
@@ -74,20 +38,29 @@ external interrupt_setup : opaque (* Uint32Array *) -> unit = "interrupt_setup"
7438

7539
let interrupt_is_setup = ref false
7640

41+
let log_interrupt () =
42+
let lvl, message =
43+
if not !interrupt_is_setup then
44+
(* Maybe set one step mode, but usually that's done in the client. *)
45+
(Lsp.Io.Lvl.Error, "Interrupt is not setup: Functionality will suffer")
46+
else (Lsp.Io.Lvl.Info, "Interrupt setup: [Control.interrupt] backend")
47+
in
48+
Lsp.Io.logMessage ~lvl ~message
49+
7750
let parse_msg msg =
78-
if Js.instanceof msg Js.array_length then (
51+
if Js.instanceof msg Js.array_empty then (
7952
let _method_ = Js.array_get msg 0 in
8053
let handle = Js.array_get msg 1 |> Obj.magic in
8154
interrupt_setup handle;
8255
interrupt_is_setup := true;
8356
Error "processed interrupt_setup")
84-
else obj_to_json msg |> Lsp.Base.Message.of_yojson
57+
else Jsso.obj_to_json msg |> Lsp.Base.Message.of_yojson
8558

8659
let on_msg msg =
8760
match parse_msg msg with
8861
| Error _ ->
89-
Lsp.Io.logMessage ~lvl:Lsp.Io.Lvl.Error
90-
~message:"Error in JSON RPC Message Parsing"
62+
let message = "Error in JSON RPC Message Parsing" in
63+
Lsp.Io.logMessage ~lvl:Lsp.Io.Lvl.Error ~message
9164
| Ok msg ->
9265
(* Lsp.Io.trace "interrupt_setup" (string_of_bool !interrupt_is_setup); *)
9366
Lsp_core.enqueue_message msg
@@ -112,14 +85,17 @@ let rec process_queue ~state () =
11285

11386
let on_init ~io ~root_state ~cmdline ~debug msg =
11487
match parse_msg msg with
115-
| Error _ -> ()
88+
| Error _ ->
89+
(* This is called one for interrupt setup *)
90+
()
11691
| Ok msg -> (
11792
match
11893
Lsp_core.lsp_init_process ~ofn:post_message ~io ~cmdline ~debug msg
11994
with
12095
| Lsp_core.Init_effect.Exit -> (* XXX: bind to worker.close () *) ()
12196
| Lsp_core.Init_effect.Loop -> ()
12297
| Lsp_core.Init_effect.Success workspaces ->
98+
log_interrupt ();
12399
Worker.set_onmessage on_msg;
124100
let default_workspace = Coq.Workspace.default ~debug ~cmdline in
125101
let state =

controller-js/jsso.ml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
open Js_of_ocaml
2+
3+
let rec obj_to_json (cobj : < .. > Js.t) : Yojson.Safe.t =
4+
let open Js in
5+
let open Js.Unsafe in
6+
let typeof_cobj = to_string (typeof cobj) in
7+
match typeof_cobj with
8+
| "string" -> `String (to_string @@ coerce cobj)
9+
| "boolean" -> `Bool (to_bool @@ coerce cobj)
10+
| "number" -> `Int (int_of_float @@ float_of_number @@ coerce cobj)
11+
| _ ->
12+
if instanceof cobj array_empty then
13+
`List Array.(to_list @@ map obj_to_json @@ to_array @@ coerce cobj)
14+
else if instanceof cobj Typed_array.arrayBuffer then
15+
`String (Typed_array.String.of_arrayBuffer @@ coerce cobj)
16+
else if instanceof cobj Typed_array.uint8Array then
17+
`String (Typed_array.String.of_uint8Array @@ coerce cobj)
18+
(* Careful in case we miss cases here, what about '{}' for example, we
19+
should also stop on functions? *)
20+
else if instanceof cobj Unsafe.global##._Object then
21+
Js.array_map
22+
(fun key -> (Js.to_string key, obj_to_json (Js.Unsafe.get cobj key)))
23+
(Js.object_keys cobj)
24+
|> Js.to_array |> Array.to_list
25+
|> fun al -> `Assoc al
26+
else if Js.Opt.(strict_equals (some cobj) null) then `Null
27+
else if Js.Optdef.(strict_equals (def cobj) undefined) then (
28+
Firebug.console##info "undefined branch!!!!";
29+
`Null)
30+
else (
31+
Firebug.console##error "failure in coq_lsp_worker:obj_to_json";
32+
Firebug.console##error cobj;
33+
Firebug.console##error (Json.output cobj);
34+
raise (Failure "coq_lsp_worker:obj_to_json"))
35+
36+
(* Old code, which is only useful for debug *)
37+
(* let json_string = Js.to_string (Json.output cobj) in *)
38+
(* Yojson.Safe.from_string json_string *)
39+
40+
let rec json_to_obj (cobj : < .. > Js.t) (json : Yojson.Safe.t) : < .. > Js.t =
41+
let open Js.Unsafe in
42+
let ofresh j = json_to_obj (obj [||]) j in
43+
match json with
44+
| `Bool b -> coerce @@ Js.bool b
45+
| `Null -> pure_js_expr "null"
46+
| `Assoc l ->
47+
List.iter (fun (p, js) -> set cobj p (ofresh js)) l;
48+
coerce @@ cobj
49+
| `List l -> coerce @@ Array.(Js.array @@ map ofresh (of_list l))
50+
| `Float f -> coerce @@ Js.number_of_float f
51+
| `String s -> coerce @@ Js.string s
52+
| `Int m -> coerce @@ Js.number_of_float (float_of_int m)
53+
| `Intlit s -> coerce @@ Js.number_of_float (float_of_string s)
54+
| `Tuple t -> coerce @@ Array.(Js.array @@ map ofresh (of_list t))
55+
| `Variant (_, _) -> pure_js_expr "undefined"
56+
57+
let json_to_obj json = json_to_obj (Js.Unsafe.obj [||]) json

controller-js/jsso.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
open Js_of_ocaml
2+
3+
(* Object to Yojson converter *)
4+
val obj_to_json : < .. > Js.t -> Yojson.Safe.t
5+
val json_to_obj : Yojson.Safe.t -> < .. > Js.t

controller/lsp_core.ml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,20 @@ let do_document = do_document_request_maybe ~handler:Rq_document.request
416416
let do_save_vo = do_document_request_maybe ~handler:Rq_save.request
417417
let do_lens = do_document_request_maybe ~handler:Rq_lens.request
418418

419+
(* could be smarter *)
420+
let do_action ~params =
421+
let range = field "range" params in
422+
match Lsp.JLang.Diagnostic.Range.of_yojson range with
423+
| Ok range ->
424+
let range = Lsp.JLang.Diagnostic.Range.vnoc range in
425+
do_immediate ~params ~handler:(Rq_action.request ~range)
426+
| Error err ->
427+
(* XXX: We really need to fix the parsing error handling in lsp_core, we got
428+
it right in petanque haha *)
429+
(* JSON-RPC Parse error (EJGA: is this the right code) *)
430+
let code = -32700 in
431+
Rq.Action.error (code, err)
432+
419433
let do_cancel ~ofn_rq ~params =
420434
let id = int_field "id" params in
421435
let code = -32800 in
@@ -584,6 +598,7 @@ let dispatch_request ~token ~method_ ~params : Rq.Action.t =
584598
| "textDocument/hover" -> do_hover ~params
585599
| "textDocument/codeLens" -> do_lens ~params
586600
| "textDocument/selectionRange" -> do_selectionRange ~params
601+
| "textDocument/codeAction" -> do_action ~params
587602
(* Proof-specific stuff *)
588603
| "proof/goals" -> do_goals ~params
589604
(* Proof-specific stuff *)

controller/request.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module R = struct
2626

2727
let print_err ~name e =
2828
match e with
29-
| Coq.Protect.Error.Anomaly (_loc, msg) | User (_loc, msg) ->
29+
| Coq.Protect.Error.Anomaly { msg; _ } | User { msg; _ } ->
3030
Format.asprintf "Error in %s request: %a" name Pp.pp_with msg
3131

3232
let of_execution ~name ~f x : t =

controller/rq_action.ml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
let point_lt { Lang.Point.line = l1; Lang.Point.character = c1; offset = _ }
2+
{ Lang.Point.line = l2; Lang.Point.character = c2; offset = _ } =
3+
l1 < l2 || (l1 = l2 && c1 < c2)
4+
5+
let point_gt { Lang.Point.line = l1; Lang.Point.character = c1; offset = _ }
6+
{ Lang.Point.line = l2; Lang.Point.character = c2; offset = _ } =
7+
l1 > l2 || (l1 = l2 && c1 > c2)
8+
9+
(* To move to doc.ml *)
10+
let filter_map_range ~range ~nodes ~f =
11+
let rec aux (nodes : Fleche.Doc.Node.t list) acc =
12+
match nodes with
13+
| [] -> List.rev acc
14+
| node :: nodes -> (
15+
let open Lang.Range in
16+
let nrange = node.range in
17+
if point_lt nrange.end_ range.start then aux nodes acc
18+
else if point_gt nrange.start range.end_ then List.rev acc
19+
else
20+
(* Node in scope *)
21+
match f node with
22+
| Some res -> aux nodes (res :: acc)
23+
| None -> aux nodes acc)
24+
in
25+
aux nodes []
26+
27+
(* util *)
28+
let filter_map_cut f l =
29+
match List.filter_map f l with
30+
| [] -> None
31+
| res -> Some res
32+
33+
(* Return list of pairs of diags, qf *)
34+
let get_qf (d : Lang.Diagnostic.t) : _ option =
35+
Option.bind d.data (function
36+
| { Lang.Diagnostic.Data.quickFix = Some qf; _ } -> Some (d, qf)
37+
| _ -> None)
38+
39+
let get_qfs ~range (doc : Fleche.Doc.t) =
40+
let f { Fleche.Doc.Node.diags; _ } = filter_map_cut get_qf diags in
41+
filter_map_range ~range ~nodes:doc.nodes ~f |> List.concat
42+
43+
let request ~range ~token:_ ~(doc : Fleche.Doc.t) =
44+
let kind = Some "quickfix" in
45+
let isPreferred = Some true in
46+
let qf = get_qfs ~range doc in
47+
let bf (d, qf) =
48+
List.map
49+
(fun { Lang.Qf.range; newText } ->
50+
let oldText =
51+
Fleche.Contents.extract_raw ~contents:doc.contents ~range
52+
in
53+
let title = Format.asprintf "Replace `%s` by `%s`" oldText newText in
54+
let diagnostics = [ d ] in
55+
let qf = [ Lsp.Workspace.TextEdit.{ range; newText } ] in
56+
let changes = [ (doc.uri, qf) ] in
57+
let edit = Some Lsp.Workspace.WorkspaceEdit.{ changes } in
58+
Lsp.Core.CodeAction.{ title; kind; diagnostics; isPreferred; edit })
59+
qf
60+
in
61+
List.concat_map bf qf
62+
63+
let request ~range ~token ~(doc : Fleche.Doc.t) =
64+
let res = request ~range ~token ~doc in
65+
Ok (`List (List.map Lsp.Core.CodeAction.to_yojson res))

controller/rq_action.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val request : range:Lang.Range.t -> Request.document

0 commit comments

Comments
 (0)