Skip to content

Commit 6ae0813

Browse files
authored
test(code-actions): render quick-fix edits as source (#2179)
Show the source produced by module qualification, missing-rec, inferred-interface, and incremental combine-cases actions instead of requiring readers to apply JSON edits mentally. Retain raw actions for diagnostic attachment, preferred-action metadata, command ranges, and unavailable-action cases. Keep numeric ranges where recovery or out-of-source positions cannot be represented by an inline marker, and keep the edited document version visible in the incremental combine-cases check. Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent 9eaa4e0 commit 6ae0813

1 file changed

Lines changed: 60 additions & 232 deletions

File tree

ocaml-lsp-server/test/e2e-new/code_actions_quick_fixes.ml

Lines changed: 60 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ let diagnostic ?(severity = DiagnosticSeverity.Error) message range =
2727
Diagnostic.create ~message:(`String message) ~range ~severity ~source:"ocamllsp" ()
2828
;;
2929

30+
let print_applied_action ?diagnostics ~title source range =
31+
match apply_code_action ~path:"test.ml" ?diagnostics title source range with
32+
| None -> print_endline "None"
33+
| Some source -> print_string source
34+
;;
35+
3036
let print_inferred_intf_edits source path range =
3137
iter_code_actions ~path ~source range (function
3238
| None -> print_endline "No code actions"
@@ -36,40 +42,15 @@ let print_inferred_intf_edits source path range =
3642
| Some (`Command _) -> print_endline "Inferred interface action was a command"
3743
| Some (`CodeAction { edit = None; _ }) -> print_endline "No edit"
3844
| Some (`CodeAction { edit = Some edit; _ }) ->
39-
let edits =
40-
Option.value edit.documentChanges ~default:[]
41-
|> List.filter_map ~f:(function
42-
| `TextDocumentEdit (text_document_edit : TextDocumentEdit.t) ->
43-
Some
44-
(`List
45-
(List.map text_document_edit.edits ~f:(function
46-
| `TextEdit edit -> TextEdit.yojson_of_t edit
47-
| `AnnotatedTextEdit edit -> AnnotatedTextEdit.yojson_of_t edit
48-
| `SnippetTextEdit edit -> SnippetTextEdit.yojson_of_t edit)))
49-
| `CreateFile _ | `RenameFile _ | `DeleteFile _ -> None)
50-
in
51-
Test.print_result (`List edits)))
45+
Test.apply_workspace_edit source edit |> print_string))
5246
;;
5347

5448
let%expect_test "opens the implementation if not in store" =
5549
let dir = setup_inferred_intf_workspace () in
5650
let path = Filename.concat dir "lib.mli" in
5751
let range = range ~start_line:0 ~start_character:0 ~end_line:0 ~end_character:0 in
5852
print_inferred_intf_edits "" path range;
59-
[%expect
60-
{|
61-
[
62-
[
63-
{
64-
"newText": "val x : int\n",
65-
"range": {
66-
"end": { "character": 0, "line": 0 },
67-
"start": { "character": 0, "line": 0 }
68-
}
69-
}
70-
]
71-
]
72-
|}]
53+
[%expect {| val x : int |}]
7354
;;
7455

7556
let%expect_test "offers Construct an expression code action" =
@@ -106,39 +87,17 @@ let y = M.f M.a
10687
|ocaml}
10788
in
10889
let range = range ~start_line:6 ~start_character:5 ~end_line:6 ~end_character:5 in
109-
print_code_actions
110-
~path:"test.ml"
111-
~filter:(action_title "Remove module name from identifiers")
112-
source
113-
range;
90+
print_applied_action ~title:"Remove module name from identifiers" source range;
11491
[%expect
11592
{|
116-
Code actions:
117-
{
118-
"edit": {
119-
"changes": {
120-
"file:///test.ml": [
121-
{
122-
"newText": "f",
123-
"range": {
124-
"end": { "character": 11, "line": 7 },
125-
"start": { "character": 8, "line": 7 }
126-
}
127-
},
128-
{
129-
"newText": "a",
130-
"range": {
131-
"end": { "character": 15, "line": 7 },
132-
"start": { "character": 12, "line": 7 }
133-
}
134-
}
135-
]
136-
}
137-
},
138-
"isPreferred": false,
139-
"kind": "remove module name from identifiers",
140-
"title": "Remove module name from identifiers"
141-
}
93+
module M = struct
94+
let a = 1
95+
let f x = x + 1
96+
end
97+
98+
open M
99+
100+
let y = f a
142101
|}]
143102
;;
144103

@@ -155,39 +114,17 @@ let y = f a
155114
|ocaml}
156115
in
157116
let range = range ~start_line:6 ~start_character:5 ~end_line:6 ~end_character:5 in
158-
print_code_actions
159-
~path:"test.ml"
160-
~filter:(action_title "Put module name in identifiers")
161-
source
162-
range;
117+
print_applied_action ~title:"Put module name in identifiers" source range;
163118
[%expect
164119
{|
165-
Code actions:
166-
{
167-
"edit": {
168-
"changes": {
169-
"file:///test.ml": [
170-
{
171-
"newText": "M.f",
172-
"range": {
173-
"end": { "character": 9, "line": 7 },
174-
"start": { "character": 8, "line": 7 }
175-
}
176-
},
177-
{
178-
"newText": "M.a",
179-
"range": {
180-
"end": { "character": 11, "line": 7 },
181-
"start": { "character": 10, "line": 7 }
182-
}
183-
}
184-
]
185-
}
186-
},
187-
"isPreferred": false,
188-
"kind": "put module name in identifiers",
189-
"title": "Put module name in identifiers"
190-
}
120+
module M = struct
121+
let a = 1
122+
let f x = x + 1
123+
end
124+
125+
open M
126+
127+
let y = M.f M.a
191128
|}]
192129
;;
193130

@@ -203,48 +140,8 @@ let%expect_test "add missing rec in toplevel let" =
203140
]
204141
in
205142
let range = range ~start_line:0 ~start_character:31 ~end_line:0 ~end_character:32 in
206-
print_code_actions
207-
~path:"missing-rec-1.ml"
208-
~diagnostics
209-
~filter:add_rec_action
210-
source
211-
range;
212-
[%expect
213-
{|
214-
Code actions:
215-
{
216-
"diagnostics": [
217-
{
218-
"message": "Unbound value",
219-
"range": {
220-
"end": { "character": 32, "line": 0 },
221-
"start": { "character": 23, "line": 0 }
222-
},
223-
"severity": 1,
224-
"source": "ocamllsp"
225-
}
226-
],
227-
"edit": {
228-
"documentChanges": [
229-
{
230-
"edits": [
231-
{
232-
"newText": "rec ",
233-
"range": {
234-
"end": { "character": 4, "line": 0 },
235-
"start": { "character": 4, "line": 0 }
236-
}
237-
}
238-
],
239-
"textDocument": { "uri": "file:///missing-rec-1.ml", "version": 0 }
240-
}
241-
]
242-
},
243-
"isPreferred": false,
244-
"kind": "quickfix",
245-
"title": "Add missing `rec` keyword"
246-
}
247-
|}]
143+
print_applied_action ~diagnostics ~title:"Add missing `rec` keyword" source range;
144+
[%expect {| let rec needs_rec x = 1 + (needs_rec x) |}]
248145
;;
249146

250147
let%expect_test "add missing rec in expression let" =
@@ -261,47 +158,12 @@ let%expect_test "add missing rec in expression let" =
261158
]
262159
in
263160
let range = range ~start_line:2 ~start_character:14 ~end_line:2 ~end_character:15 in
264-
print_code_actions
265-
~path:"missing-rec-2.ml"
266-
~diagnostics
267-
~filter:add_rec_action
268-
source
269-
range;
161+
print_applied_action ~diagnostics ~title:"Add missing `rec` keyword" source range;
270162
[%expect
271163
{|
272-
Code actions:
273-
{
274-
"diagnostics": [
275-
{
276-
"message": "Unbound value",
277-
"range": {
278-
"end": { "character": 14, "line": 2 },
279-
"start": { "character": 9, "line": 2 }
280-
},
281-
"severity": 1,
282-
"source": "ocamllsp"
283-
}
284-
],
285-
"edit": {
286-
"documentChanges": [
287-
{
288-
"edits": [
289-
{
290-
"newText": "rec ",
291-
"range": {
292-
"end": { "character": 6, "line": 1 },
293-
"start": { "character": 6, "line": 1 }
294-
}
295-
}
296-
],
297-
"textDocument": { "uri": "file:///missing-rec-2.ml", "version": 0 }
298-
}
299-
]
300-
},
301-
"isPreferred": false,
302-
"kind": "quickfix",
303-
"title": "Add missing `rec` keyword"
304-
}
164+
let outer =
165+
let rec inner x =
166+
1 + (inner
305167
|}]
306168
;;
307169

@@ -320,47 +182,13 @@ let%expect_test "add missing rec in expression let-and" =
320182
]
321183
in
322184
let range = range ~start_line:3 ~start_character:14 ~end_line:3 ~end_character:15 in
323-
print_code_actions
324-
~path:"missing-rec-3.ml"
325-
~diagnostics
326-
~filter:add_rec_action
327-
source
328-
range;
185+
print_applied_action ~diagnostics ~title:"Add missing `rec` keyword" source range;
329186
[%expect
330187
{|
331-
Code actions:
332-
{
333-
"diagnostics": [
334-
{
335-
"message": "Unbound value",
336-
"range": {
337-
"end": { "character": 14, "line": 3 },
338-
"start": { "character": 9, "line": 3 }
339-
},
340-
"severity": 1,
341-
"source": "ocamllsp"
342-
}
343-
],
344-
"edit": {
345-
"documentChanges": [
346-
{
347-
"edits": [
348-
{
349-
"newText": "rec ",
350-
"range": {
351-
"end": { "character": 6, "line": 1 },
352-
"start": { "character": 6, "line": 1 }
353-
}
354-
}
355-
],
356-
"textDocument": { "uri": "file:///missing-rec-3.ml", "version": 0 }
357-
}
358-
]
359-
},
360-
"isPreferred": false,
361-
"kind": "quickfix",
362-
"title": "Add missing `rec` keyword"
363-
}
188+
let outer =
189+
let rec inner1 = 0
190+
and inner x =
191+
1 + (inner
364192
|}]
365193
;;
366194

@@ -620,31 +448,31 @@ let f = function
620448
in
621449
let params = CodeActionParams.create ~textDocument ~range:query_range ~context () in
622450
let* response = Client.request client (CodeAction params) in
623-
print_code_action_result ~filter:(find_action "combine-cases") response;
451+
let edit =
452+
Option.value_exn response
453+
|> List.find_map ~f:(function
454+
| `CodeAction { CodeAction.title = "Combine-cases"; edit = Some edit; _ } ->
455+
Some edit
456+
| `CodeAction _ | `Command _ -> None)
457+
|> Option.value_exn
458+
in
459+
let version =
460+
match edit.documentChanges with
461+
| Some [ `TextDocumentEdit { textDocument = { version; _ }; _ } ] ->
462+
Option.value_exn version
463+
| None | Some _ -> failwith "expected one versioned document edit"
464+
in
465+
Printf.printf "edit version: %d\n" version;
466+
let source =
467+
Test.apply_edits source [ TextEdit.create ~range:edit_range ~newText:" " ]
468+
in
469+
Test.apply_workspace_edit source edit |> print_string;
624470
Test.exit_client client);
625471
[%expect
626472
{|
627-
Code actions:
628-
{
629-
"edit": {
630-
"documentChanges": [
631-
{
632-
"edits": [
633-
{
634-
"newText": " | A | B -> 1\n",
635-
"range": {
636-
"end": { "character": 0, "line": 4 },
637-
"start": { "character": 0, "line": 2 }
638-
}
639-
}
640-
],
641-
"textDocument": { "uri": "file:///test.ml", "version": 1 }
642-
}
643-
]
644-
},
645-
"isPreferred": false,
646-
"kind": "combine-cases",
647-
"title": "Combine-cases"
648-
}
473+
edit version: 1
474+
type t = A | B
475+
let f = function
476+
| A | B -> 1
649477
|}]
650478
;;

0 commit comments

Comments
 (0)