Skip to content

Commit dbf854e

Browse files
authored
fix: correctly use merlin's pipeline (#904)
do not reuse pipelines unless we are using the same pipeline multiple times in a row Signed-off-by: Rudi Grinberg <[email protected]>
1 parent 51e2ef0 commit dbf854e

File tree

8 files changed

+118
-95
lines changed

8 files changed

+118
-95
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Fixes
44

5+
- Fix random requests failing after switching documents (#904, fixes #898)
6+
57
- Do not offer related diagnostic information unless the user enables in client
68
capabilities (#905)
79

ocaml-lsp-server/src/document.ml

Lines changed: 95 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -86,36 +86,6 @@ module Syntax = struct
8686
| None -> Text_document.documentUri td |> Uri.to_path |> of_fname
8787
end
8888

89-
type merlin =
90-
{ tdoc : Text_document.t
91-
; pipeline : Mpipeline.t Lazy_fiber.t
92-
; merlin : Lev_fiber.Thread.t
93-
; timer : Lev_fiber.Timer.Wheel.task
94-
; merlin_config : Merlin_config.t
95-
; syntax : Syntax.t
96-
}
97-
98-
type t =
99-
| Other of
100-
{ tdoc : Text_document.t
101-
; syntax : Syntax.t
102-
}
103-
| Merlin of merlin
104-
105-
let tdoc = function
106-
| Other d -> d.tdoc
107-
| Merlin m -> m.tdoc
108-
109-
let uri t = Text_document.documentUri (tdoc t)
110-
111-
let syntax = function
112-
| Merlin m -> m.syntax
113-
| Other t -> t.syntax
114-
115-
let text t = Text_document.text (tdoc t)
116-
117-
let source t = Msource.make (text t)
118-
11989
let await task =
12090
let* cancel_token = Server.cancel_token () in
12191
let f () = Lev_fiber.Thread.await task in
@@ -146,40 +116,112 @@ let await task =
146116
in
147117
raise (Jsonrpc.Response.Error.E e))
148118

149-
let version t = Text_document.version (tdoc t)
119+
module Single_pipeline : sig
120+
type t
121+
122+
val create : Lev_fiber.Thread.t -> t
150123

151-
let make_pipeline merlin_config thread tdoc =
152-
Lazy_fiber.create (fun () ->
153-
let* config = Merlin_config.config merlin_config in
154-
let* async_make_pipeline =
155-
match
156-
Lev_fiber.Thread.task thread ~f:(fun () ->
157-
Text_document.text tdoc |> Msource.make |> Mpipeline.make config)
158-
with
159-
| Error `Stopped -> Fiber.never
160-
| Ok task -> Fiber.return task
124+
val use :
125+
t
126+
-> doc:Text_document.t
127+
-> config:Merlin_config.t
128+
-> f:(Mpipeline.t -> 'a)
129+
-> ('a, Exn_with_backtrace.t) result Fiber.t
130+
end = struct
131+
type t =
132+
{ thread : Lev_fiber.Thread.t
133+
; mutable last : (Text_document.t * Mconfig.t * Mpipeline.t) option
134+
}
135+
136+
let create thread = { thread; last = None }
137+
138+
let use t ~doc ~config ~f =
139+
let* config = Merlin_config.config config in
140+
let make_pipeline =
141+
match t.last with
142+
| Some (doc', config', pipeline) when doc' == doc && config == config' ->
143+
fun () -> pipeline
144+
| _ ->
145+
let source = Msource.make (Text_document.text doc) in
146+
fun () -> Mpipeline.make config source
147+
in
148+
let task =
149+
match
150+
Lev_fiber.Thread.task t.thread ~f:(fun () ->
151+
let start = Unix.time () in
152+
let pipeline = make_pipeline () in
153+
let res = Mpipeline.with_pipeline pipeline (fun () -> f pipeline) in
154+
let stop = Unix.time () in
155+
(res, pipeline, start, stop))
156+
with
157+
| Error `Stopped -> assert false
158+
| Ok task -> task
159+
in
160+
let* res = await task in
161+
match res with
162+
| Error exn -> Fiber.return (Error exn)
163+
| Ok (res, pipeline, start, stop) ->
164+
let event =
165+
let module Event = Chrome_trace.Event in
166+
let dur = Event.Timestamp.of_float_seconds (stop -. start) in
167+
let fields =
168+
Event.common_fields
169+
~ts:(Event.Timestamp.of_float_seconds start)
170+
~name:"merlin"
171+
()
172+
in
173+
Event.complete ~dur fields
161174
in
162-
let+ res = await async_make_pipeline in
163-
match res with
164-
| Ok s -> s
165-
| Error e -> Exn_with_backtrace.reraise e)
175+
t.last <- Some (doc, config, pipeline);
176+
let+ () = Metrics.report event in
177+
Ok res
178+
end
166179

167-
let make_merlin wheel merlin_db ~merlin_thread tdoc syntax =
180+
type merlin =
181+
{ tdoc : Text_document.t
182+
; pipeline : Single_pipeline.t
183+
; timer : Lev_fiber.Timer.Wheel.task
184+
; merlin_config : Merlin_config.t
185+
; syntax : Syntax.t
186+
}
187+
188+
type t =
189+
| Other of
190+
{ tdoc : Text_document.t
191+
; syntax : Syntax.t
192+
}
193+
| Merlin of merlin
194+
195+
let tdoc = function
196+
| Other d -> d.tdoc
197+
| Merlin m -> m.tdoc
198+
199+
let uri t = Text_document.documentUri (tdoc t)
200+
201+
let syntax = function
202+
| Merlin m -> m.syntax
203+
| Other t -> t.syntax
204+
205+
let text t = Text_document.text (tdoc t)
206+
207+
let source t = Msource.make (text t)
208+
209+
let version t = Text_document.version (tdoc t)
210+
211+
let make_merlin wheel merlin_db pipeline tdoc syntax =
168212
let+ timer = Lev_fiber.Timer.Wheel.task wheel in
169213
let merlin_config =
170214
let uri = Text_document.documentUri tdoc in
171215
Merlin_config.DB.get merlin_db uri
172216
in
173-
let pipeline = make_pipeline merlin_config merlin_thread tdoc in
174-
Merlin
175-
{ merlin_config; tdoc; pipeline; merlin = merlin_thread; timer; syntax }
217+
Merlin { merlin_config; tdoc; pipeline; timer; syntax }
176218

177-
let make wheel config ~merlin_thread (doc : DidOpenTextDocumentParams.t) =
219+
let make wheel config pipeline (doc : DidOpenTextDocumentParams.t) =
178220
Fiber.of_thunk (fun () ->
179221
let tdoc = Text_document.make doc in
180222
let syntax = Syntax.of_text_document tdoc in
181223
match syntax with
182-
| Ocaml | Reason -> make_merlin wheel config ~merlin_thread tdoc syntax
224+
| Ocaml | Reason -> make_merlin wheel config pipeline tdoc syntax
183225
| Ocamllex | Menhir | Cram | Dune -> Fiber.return (Other { tdoc; syntax }))
184226

185227
let update_text ?version t changes =
@@ -200,9 +242,7 @@ let update_text ?version t changes =
200242
| tdoc -> (
201243
match t with
202244
| Other o -> Other { o with tdoc }
203-
| Merlin ({ merlin_config; merlin; _ } as t) ->
204-
let pipeline = make_pipeline merlin_config merlin tdoc in
205-
Merlin { t with tdoc; pipeline })
245+
| Merlin t -> Merlin { t with tdoc })
206246

207247
module Merlin = struct
208248
type t = merlin
@@ -216,35 +256,7 @@ module Merlin = struct
216256
let kind t = Kind.of_fname (Uri.to_path (uri (Merlin t)))
217257

218258
let with_pipeline (t : t) f =
219-
let* pipeline = Lazy_fiber.force t.pipeline in
220-
let* task =
221-
match
222-
Lev_fiber.Thread.task t.merlin ~f:(fun () ->
223-
let start = Unix.time () in
224-
let res = Mpipeline.with_pipeline pipeline (fun () -> f pipeline) in
225-
let stop = Unix.time () in
226-
let event =
227-
let module Event = Chrome_trace.Event in
228-
let dur = Event.Timestamp.of_float_seconds (stop -. start) in
229-
let fields =
230-
Event.common_fields
231-
~ts:(Event.Timestamp.of_float_seconds start)
232-
~name:"merlin"
233-
()
234-
in
235-
Event.complete ~dur fields
236-
in
237-
(event, res))
238-
with
239-
| Error `Stopped -> Fiber.never
240-
| Ok task -> Fiber.return task
241-
in
242-
let* res = await task in
243-
match res with
244-
| Ok (event, result) ->
245-
let+ () = Metrics.report event in
246-
Ok result
247-
| Error e -> Fiber.return (Error e)
259+
Single_pipeline.use t.pipeline ~doc:t.tdoc ~config:t.merlin_config ~f
248260

249261
let with_pipeline_exn doc f =
250262
let+ res = with_pipeline doc f in

ocaml-lsp-server/src/document.mli

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ end
2424

2525
val syntax : t -> Syntax.t
2626

27+
module Single_pipeline : sig
28+
type t
29+
30+
val create : Lev_fiber.Thread.t -> t
31+
end
32+
2733
val make :
2834
Lev_fiber.Timer.Wheel.t
2935
-> Merlin_config.DB.t
30-
-> merlin_thread:Lev_fiber.Thread.t
36+
-> Single_pipeline.t
3137
-> DidOpenTextDocumentParams.t
3238
-> t Fiber.t
3339

ocaml-lsp-server/src/inference.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ let open_document_from_file (state : State.t) uri =
5656
Document.make
5757
(State.wheel state)
5858
state.merlin_config
59-
~merlin_thread:state.merlin
59+
state.merlin
6060
params
6161
in
6262
Some doc)

ocaml-lsp-server/src/ocaml_lsp_server.ml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -906,11 +906,7 @@ let on_notification server (notification : Client_notification.t) :
906906
match notification with
907907
| TextDocumentDidOpen params ->
908908
let* doc =
909-
Document.make
910-
(State.wheel state)
911-
state.merlin_config
912-
params
913-
~merlin_thread:state.merlin
909+
Document.make (State.wheel state) state.merlin_config state.merlin params
914910
in
915911
assert (Document_store.get_opt store params.textDocument.uri = None);
916912
let* () = Document_store.open_document store doc in

ocaml-lsp-server/src/state.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type init =
1212

1313
type t =
1414
{ store : Document_store.t
15-
; merlin : Lev_fiber.Thread.t
15+
; merlin : Document.Single_pipeline.t
1616
; merlin_config : Merlin_config.DB.t
1717
; init : init
1818
; detached : Fiber.Pool.t
@@ -28,7 +28,7 @@ let create ~store ~merlin ~detached ~configuration ~ocamlformat_rpc
2828
{ init = Uninitialized
2929
; merlin_config = Merlin_config.DB.create ()
3030
; store
31-
; merlin
31+
; merlin = Document.Single_pipeline.create merlin
3232
; detached
3333
; configuration
3434
; trace = Off

ocaml-lsp-server/src/state.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type init =
1212

1313
type t =
1414
{ store : Document_store.t
15-
; merlin : Lev_fiber.Thread.t
15+
; merlin : Document.Single_pipeline.t
1616
; merlin_config : Merlin_config.DB.t
1717
; init : init
1818
; detached : Fiber.Pool.t

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
open Test.Import
22

33
let iter_code_actions ?(path = "foo.ml") ~source range k =
4+
let diagnostics = Fiber.Ivar.create () in
45
let handler =
5-
Client.Handler.make ~on_notification:(fun _ _ -> Fiber.return ()) ()
6+
Client.Handler.make
7+
~on_notification:
8+
(fun _ -> function
9+
| PublishDiagnostics _ -> Fiber.Ivar.fill diagnostics ()
10+
| _ -> Fiber.return ())
11+
()
612
in
713
Test.run ~handler @@ fun client ->
814
let run_client () =
@@ -38,7 +44,8 @@ let iter_code_actions ?(path = "foo.ml") ~source range k =
3844
in
3945
k resp
4046
in
41-
Fiber.fork_and_join_unit run_client (fun () -> run >>> Client.stop client)
47+
Fiber.fork_and_join_unit run_client (fun () ->
48+
run >>> Fiber.Ivar.read diagnostics >>> Client.stop client)
4249

4350
let print_code_actions ?(path = "foo.ml") source range =
4451
iter_code_actions ~path ~source range (function

0 commit comments

Comments
 (0)