forked from ocaml/merlin
-
Notifications
You must be signed in to change notification settings - Fork 6
/
mpipeline.ml
197 lines (171 loc) · 5.95 KB
/
mpipeline.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
open Std
let {Logger. log} = Logger.for_section "Pipeline"
let time_shift = ref 0.0
let timed_lazy r x =
lazy (
let start = Misc.time_spent () in
let time_shift0 = !time_shift in
let update () =
let delta = Misc.time_spent () -. start in
let shift = !time_shift -. time_shift0 in
time_shift := time_shift0 +. delta;
r := !r +. delta -. shift;
in
match Lazy.force x with
| x -> update (); x
| exception exn -> update (); Std.reraise exn
)
module Cache = struct
let cache = ref []
(* Values from configuration that are used as a key for the cache.
These values should:
- allow to maximize reuse; associating a single typechecker instance to a
filename and directory is natural, but keying also based on verbosity
makes no sense
- prevent reuse in different environments (if there is a change in
loadpath, a new typechecker should be produced).
It would be better to guarantee that the typechecker was well-behaved
when the loadpath changes (so that we can reusing the same instance, and
let the typechecker figure which part of its internal state should be
invalidated).
However we already had many bug related to that. There are subtle changes
in the type checker behavior accross the different versions of OCaml.
It is simpler to create new instances upfront.
*)
let key config =
Mconfig.(
config.query.filename,
config.query.directory,
config.ocaml,
{config.merlin with log_file = None; log_sections = []}
)
let get config =
let title = "pop_cache" in
let key = key config in
match List.assoc key !cache with
| state ->
cache := (key, state) :: List.remove_assoc key !cache;
log ~title "found entry for this configuration";
state
| exception Not_found ->
log ~title "nothing cached for this configuration";
let state = Mocaml.new_state () in
cache := (key, state) :: List.take_n 5 !cache;
state
end
module Typer = struct
type t = {
errors : exn list lazy_t;
result : Mtyper.result;
}
end
module Ppx = struct
type t = {
config : Mconfig.t;
errors : exn list;
parsetree : Mreader.parsetree;
}
end
type t = {
config : Mconfig.t;
state : Mocaml.typer_state;
raw_source : Msource.t;
source : (Msource.t * Mreader.parsetree option) lazy_t;
reader : (Mreader.result * Mconfig.t) lazy_t;
ppx : Ppx.t lazy_t;
typer : Typer.t lazy_t;
pp_time : float ref;
reader_time : float ref;
ppx_time : float ref;
typer_time : float ref;
error_time : float ref;
}
let raw_source t = t.raw_source
let input_config t = t.config
let input_source t = fst (Lazy.force t.source)
let with_pipeline t f =
Mocaml.with_state t.state @@ fun () ->
Mreader.with_ambient_reader t.config (input_source t) f
let get_lexing_pos t pos =
Msource.get_lexing_pos
(input_source t) ~filename:(Mconfig.filename t.config) pos
let reader t = Lazy.force t.reader
let ppx t = Lazy.force t.ppx
let typer t = Lazy.force t.typer
let reader_config t = (snd (reader t))
let reader_parsetree t = (fst (reader t)).Mreader.parsetree
let reader_comments t = (fst (reader t)).Mreader.comments
let reader_lexer_keywords t = (fst (reader t)).Mreader.lexer_keywords
let reader_lexer_errors t = (fst (reader t)).Mreader.lexer_errors
let reader_parser_errors t = (fst (reader t)).Mreader.parser_errors
let reader_no_labels_for_completion t =
(fst (reader t)).Mreader.no_labels_for_completion
let ppx_parsetree t = (ppx t).Ppx.parsetree
let ppx_errors t = (ppx t).Ppx.errors
let final_config t = (ppx t).Ppx.config
let typer_result t = (typer t).Typer.result
let typer_errors t = Lazy.force (typer t).Typer.errors
let process
?state
?(pp_time=ref 0.0)
?(reader_time=ref 0.0)
?(ppx_time=ref 0.0)
?(typer_time=ref 0.0)
?(error_time=ref 0.0)
?for_completion
config raw_source =
let state = match state with
| None -> Cache.get config
| Some state -> state
in
let source = timed_lazy pp_time (lazy (
match Mconfig.(config.ocaml.pp) with
| None -> raw_source, None
| Some { workdir; workval } ->
let source = Msource.text raw_source in
match
Pparse.apply_pp
~workdir ~filename:Mconfig.(config.query.filename)
~source ~pp:workval
with
| `Source source -> Msource.make source, None
| (`Interface _ | `Implementation _) as ast ->
raw_source, Some ast
)) in
let reader = timed_lazy reader_time (lazy (
let lazy source = source in
let config = Mconfig.normalize config in
Mocaml.setup_reader_config config;
let result = Mreader.parse ?for_completion config source in
result, config
)) in
let ppx = timed_lazy ppx_time (lazy (
let lazy ({Mreader.parsetree; _}, config) = reader in
let caught = ref [] in
Msupport.catch_errors Mconfig.(config.ocaml.warnings) caught @@ fun () ->
let parsetree = Mppx.rewrite config parsetree in
{ Ppx. config; parsetree; errors = !caught }
)) in
let typer = timed_lazy typer_time (lazy (
let lazy { Ppx. config; parsetree; _ } = ppx in
Mocaml.setup_typer_config config;
let result = Mtyper.run config parsetree in
let errors = timed_lazy error_time (lazy (Mtyper.get_errors result)) in
{ Typer. errors; result }
)) in
{ config; state; raw_source; source; reader; ppx; typer;
pp_time; reader_time; ppx_time; typer_time; error_time }
let make config source =
process (Mconfig.normalize config) source
let for_completion position
{config; state; raw_source;
pp_time; reader_time; ppx_time; typer_time; error_time; _} =
process config raw_source ~for_completion:position
~state ~pp_time ~reader_time ~ppx_time ~typer_time ~error_time
let timing_information t = [
"pp" , !(t.pp_time);
"reader" , !(t.reader_time);
"ppx" , !(t.ppx_time);
"typer" , !(t.typer_time);
"error" , !(t.error_time);
]