Skip to content

Commit b4cf816

Browse files
authored
Make code-lens for toplevel let binding configurable (#1567)
* Add a setting to enable CodeLens only for toplevel let binding. * Add tests. * Use JSON to compare code lens test output. * Take Xavier's comments into account. * Fix config infos. * Better definition of option semantics. * Update changes.md.
1 parent f03ef18 commit b4cf816

File tree

6 files changed

+177
-13
lines changed

6 files changed

+177
-13
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Unreleased
2+
3+
## Features
4+
5+
- Make `code-lens` for nested let bindings configurable (#1567)
6+
17
# 1.24.0
28

39
## Features

ocaml-lsp-server/docs/ocamllsp/config.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@ interface config {
1515
*/
1616
extendedHover: { enable : boolean }
1717

18-
/**
19-
* Enable/Disable CodeLens
20-
* @default false
21-
* @since 1.16
22-
*/
23-
codelens: { enable : boolean }
18+
codelens: {
19+
/**
20+
* Enable/Disable CodeLens
21+
* @default false
22+
* @since 1.16
23+
*/
24+
enable : boolean,
25+
26+
/**
27+
* Enable CodeLens for nested let bindings
28+
* @default false
29+
* @since 1.25
30+
*/
31+
for_nested_bindings : boolean
32+
}
2433

2534
/**
2635
* Enable/Disable Dune diagnostics

ocaml-lsp-server/src/config_data.ml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ module InlayHints = struct
192192
end
193193

194194
module Lens = struct
195-
type t = { enable : bool [@default true] }
195+
type t =
196+
{ enable : bool [@default true]
197+
; for_nested_bindings : bool [@default false]
198+
}
196199
[@@deriving_inline yojson] [@@yojson.allow_extra_fields]
197200

198201
let _ = fun (_ : t) -> ()
@@ -202,6 +205,7 @@ module Lens = struct
202205
function
203206
| `Assoc field_yojsons as yojson ->
204207
let enable_field = ref Ppx_yojson_conv_lib.Option.None
208+
and for_nested_bindings_field = ref Ppx_yojson_conv_lib.Option.None
205209
and duplicates = ref []
206210
and extra = ref [] in
207211
let rec iter = function
@@ -214,6 +218,13 @@ module Lens = struct
214218
enable_field := Ppx_yojson_conv_lib.Option.Some fvalue
215219
| Ppx_yojson_conv_lib.Option.Some _ ->
216220
duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates)
221+
| "for_nested_bindings" ->
222+
(match Ppx_yojson_conv_lib.( ! ) for_nested_bindings_field with
223+
| Ppx_yojson_conv_lib.Option.None ->
224+
let fvalue = bool_of_yojson _field_yojson in
225+
for_nested_bindings_field := Ppx_yojson_conv_lib.Option.Some fvalue
226+
| Ppx_yojson_conv_lib.Option.Some _ ->
227+
duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates)
217228
| _ -> ());
218229
iter tail
219230
| [] -> ()
@@ -233,11 +244,18 @@ module Lens = struct
233244
(Ppx_yojson_conv_lib.( ! ) extra)
234245
yojson
235246
| [] ->
236-
let enable_value = Ppx_yojson_conv_lib.( ! ) enable_field in
247+
let enable_value, for_nested_bindings_value =
248+
( Ppx_yojson_conv_lib.( ! ) enable_field
249+
, Ppx_yojson_conv_lib.( ! ) for_nested_bindings_field )
250+
in
237251
{ enable =
238252
(match enable_value with
239253
| Ppx_yojson_conv_lib.Option.None -> true
240254
| Ppx_yojson_conv_lib.Option.Some v -> v)
255+
; for_nested_bindings =
256+
(match for_nested_bindings_value with
257+
| Ppx_yojson_conv_lib.Option.None -> false
258+
| Ppx_yojson_conv_lib.Option.Some v -> v)
241259
}))
242260
| _ as yojson ->
243261
Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom _tp_loc yojson
@@ -248,8 +266,12 @@ module Lens = struct
248266

249267
let yojson_of_t =
250268
(function
251-
| { enable = v_enable } ->
269+
| { enable = v_enable; for_nested_bindings = v_for_nested_bindings } ->
252270
let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in
271+
let bnds =
272+
let arg = yojson_of_bool v_for_nested_bindings in
273+
("for_nested_bindings", arg) :: bnds
274+
in
253275
let bnds =
254276
let arg = yojson_of_bool v_enable in
255277
("enable", arg) :: bnds
@@ -921,7 +943,7 @@ let _ = yojson_of_t
921943
[@@@end]
922944

923945
let default =
924-
{ codelens = Some { enable = false }
946+
{ codelens = Some { enable = false; for_nested_bindings = false }
925947
; extended_hover = Some { enable = false }
926948
; standard_hover = Some { enable = true }
927949
; inlay_hints =

ocaml-lsp-server/src/ocaml_lsp_server.ml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,11 @@ module Formatter = struct
363363
;;
364364
end
365365

366-
let text_document_lens (state : State.t) { CodeLensParams.textDocument = { uri }; _ } =
366+
let text_document_lens
367+
(state : State.t)
368+
{ CodeLensParams.textDocument = { uri }; _ }
369+
~for_nested_bindings
370+
=
367371
let store = state.store in
368372
let doc = Document_store.get store uri in
369373
match Document.kind doc with
@@ -372,7 +376,11 @@ let text_document_lens (state : State.t) { CodeLensParams.textDocument = { uri }
372376
| `Merlin doc ->
373377
let+ outline = Document.Merlin.dispatch_exn ~name:"outline" doc Outline in
374378
let rec symbol_info_of_outline_item (item : Query_protocol.item) =
375-
let children = List.concat_map item.children ~f:symbol_info_of_outline_item in
379+
let children =
380+
if for_nested_bindings
381+
then List.concat_map item.children ~f:symbol_info_of_outline_item
382+
else []
383+
in
376384
match item.outline_type with
377385
| None -> children
378386
| Some typ ->
@@ -651,7 +659,8 @@ let on_request
651659
| TextDocumentCodeLensResolve codeLens -> now codeLens
652660
| TextDocumentCodeLens req ->
653661
(match state.configuration.data.codelens with
654-
| Some { enable = true } -> later text_document_lens req
662+
| Some { enable = true; for_nested_bindings } ->
663+
later (text_document_lens ~for_nested_bindings) req
655664
| _ -> now [])
656665
| TextDocumentHighlight req -> later highlight req
657666
| DocumentSymbol { textDocument = { uri }; _ } -> later document_symbol uri
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
open Test.Import
2+
3+
let change_config client params = Client.notification client (ChangeConfiguration params)
4+
5+
let codelens client textDocument =
6+
Client.request
7+
client
8+
(TextDocumentCodeLens
9+
{ textDocument; workDoneToken = None; partialResultToken = None })
10+
;;
11+
12+
let json_of_codelens cs = `List (List.map ~f:CodeLens.yojson_of_t cs)
13+
14+
let%expect_test "enable codelens for nested let bindings" =
15+
let source =
16+
{ocaml|
17+
let toplevel = "Hello"
18+
19+
let func x = x
20+
21+
let f x =
22+
let y = 10 in
23+
let z = 3 in
24+
x + y + z
25+
|ocaml}
26+
in
27+
let req client =
28+
let text_document = TextDocumentIdentifier.create ~uri:Helpers.uri in
29+
let* () =
30+
change_config
31+
client
32+
(DidChangeConfigurationParams.create
33+
~settings:(`Assoc [ "codelens", `Assoc [ "for_nested_bindings", `Bool true ] ]))
34+
in
35+
let* resp_codelens_toplevel = codelens client text_document in
36+
Test.print_result (json_of_codelens resp_codelens_toplevel);
37+
Fiber.return ()
38+
in
39+
Helpers.test source req;
40+
[%expect
41+
{|
42+
[
43+
{
44+
"command": { "command": "", "title": "int -> int" },
45+
"range": {
46+
"end": { "character": 11, "line": 8 },
47+
"start": { "character": 0, "line": 5 }
48+
}
49+
},
50+
{
51+
"command": { "command": "", "title": "int" },
52+
"range": {
53+
"end": { "character": 12, "line": 6 },
54+
"start": { "character": 2, "line": 6 }
55+
}
56+
},
57+
{
58+
"command": { "command": "", "title": "int" },
59+
"range": {
60+
"end": { "character": 11, "line": 7 },
61+
"start": { "character": 2, "line": 7 }
62+
}
63+
},
64+
{
65+
"command": { "command": "", "title": "'a -> 'a" },
66+
"range": {
67+
"end": { "character": 14, "line": 3 },
68+
"start": { "character": 0, "line": 3 }
69+
}
70+
},
71+
{
72+
"command": { "command": "", "title": "string" },
73+
"range": {
74+
"end": { "character": 22, "line": 1 },
75+
"start": { "character": 0, "line": 1 }
76+
}
77+
}
78+
]
79+
|}]
80+
;;
81+
82+
let%expect_test "enable codelens (default settings disable it for nested let binding)" =
83+
let source =
84+
{ocaml|
85+
let x =
86+
let y = 10 in
87+
"Hello"
88+
89+
let () = ()
90+
|ocaml}
91+
in
92+
let req client =
93+
let text_document = TextDocumentIdentifier.create ~uri:Helpers.uri in
94+
let* () =
95+
change_config
96+
client
97+
(DidChangeConfigurationParams.create
98+
~settings:(`Assoc [ "codelens", `Assoc [ "enable", `Bool true ] ]))
99+
in
100+
let* resp_codelens_toplevel = codelens client text_document in
101+
Test.print_result (json_of_codelens resp_codelens_toplevel);
102+
Fiber.return ()
103+
in
104+
Helpers.test source req;
105+
[%expect
106+
{|
107+
[
108+
{
109+
"command": { "command": "", "title": "string" },
110+
"range": {
111+
"end": { "character": 9, "line": 3 },
112+
"start": { "character": 0, "line": 1 }
113+
}
114+
}
115+
]
116+
|}]
117+
;;

ocaml-lsp-server/test/e2e-new/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
action_inline
4545
action_mark_remove
4646
code_actions
47+
code_lens
4748
completion
4849
completions
4950
construct

0 commit comments

Comments
 (0)