diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/server/src/main.rs | 184 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs (renamed from crates/server/src/handlers.rs) | 0 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 201 |
3 files changed, 206 insertions, 179 deletions
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index fe94e901e..fccfe9b3b 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs | |||
@@ -21,21 +21,17 @@ mod io; | |||
21 | mod caps; | 21 | mod caps; |
22 | mod req; | 22 | mod req; |
23 | mod dispatch; | 23 | mod dispatch; |
24 | mod handlers; | ||
25 | mod util; | 24 | mod util; |
26 | mod conv; | 25 | mod conv; |
26 | mod main_loop; | ||
27 | 27 | ||
28 | use threadpool::ThreadPool; | 28 | use threadpool::ThreadPool; |
29 | use crossbeam_channel::{bounded, Sender, Receiver}; | 29 | use crossbeam_channel::bounded; |
30 | use flexi_logger::Logger; | 30 | use flexi_logger::Logger; |
31 | use languageserver_types::Url; | 31 | use libanalysis::WorldState; |
32 | use libanalysis::{WorldState, World}; | ||
33 | 32 | ||
34 | use ::{ | 33 | use ::{ |
35 | io::{Io, RawMsg, RawRequest, RawResponse, RawNotification}, | 34 | io::{Io, RawMsg, RawResponse, RawNotification} |
36 | handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics, publish_decorations, | ||
37 | handle_document_symbol, handle_code_action}, | ||
38 | util::FilePath, | ||
39 | }; | 35 | }; |
40 | 36 | ||
41 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 37 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
@@ -121,7 +117,7 @@ fn initialized(io: &mut Io) -> Result<()> { | |||
121 | let mut pool = ThreadPool::new(4); | 117 | let mut pool = ThreadPool::new(4); |
122 | let (sender, receiver) = bounded::<Task>(16); | 118 | let (sender, receiver) = bounded::<Task>(16); |
123 | info!("lifecycle: handshake finished, server ready to serve requests"); | 119 | info!("lifecycle: handshake finished, server ready to serve requests"); |
124 | let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone()); | 120 | let res = main_loop::main_loop(io, &mut world, &mut pool, sender, receiver.clone()); |
125 | info!("waiting for background jobs to finish..."); | 121 | info!("waiting for background jobs to finish..."); |
126 | receiver.for_each(drop); | 122 | receiver.for_each(drop); |
127 | pool.join(); | 123 | pool.join(); |
@@ -142,173 +138,3 @@ fn initialized(io: &mut Io) -> Result<()> { | |||
142 | } | 138 | } |
143 | } | 139 | } |
144 | } | 140 | } |
145 | |||
146 | fn main_loop( | ||
147 | io: &mut Io, | ||
148 | world: &mut WorldState, | ||
149 | pool: &mut ThreadPool, | ||
150 | sender: Sender<Task>, | ||
151 | receiver: Receiver<Task>, | ||
152 | ) -> Result<()> { | ||
153 | info!("server initialized, serving requests"); | ||
154 | loop { | ||
155 | enum Event { | ||
156 | Msg(RawMsg), | ||
157 | Task(Task), | ||
158 | ReceiverDead, | ||
159 | } | ||
160 | |||
161 | let event = select! { | ||
162 | recv(io.receiver(), msg) => match msg { | ||
163 | Some(msg) => Event::Msg(msg), | ||
164 | None => Event::ReceiverDead, | ||
165 | }, | ||
166 | recv(receiver, task) => Event::Task(task.unwrap()), | ||
167 | }; | ||
168 | |||
169 | let msg = match event { | ||
170 | Event::ReceiverDead => { | ||
171 | io.cleanup_receiver()?; | ||
172 | unreachable!(); | ||
173 | } | ||
174 | Event::Task(task) => { | ||
175 | match task { | ||
176 | Task::Respond(response) => | ||
177 | io.send(RawMsg::Response(response)), | ||
178 | Task::Notify(n) => | ||
179 | io.send(RawMsg::Notification(n)), | ||
180 | Task::Die(error) => | ||
181 | return Err(error), | ||
182 | } | ||
183 | continue; | ||
184 | } | ||
185 | Event::Msg(msg) => msg, | ||
186 | }; | ||
187 | |||
188 | match msg { | ||
189 | RawMsg::Request(req) => { | ||
190 | let mut req = Some(req); | ||
191 | handle_request_on_threadpool::<req::SyntaxTree>( | ||
192 | &mut req, pool, world, &sender, handle_syntax_tree, | ||
193 | )?; | ||
194 | handle_request_on_threadpool::<req::ExtendSelection>( | ||
195 | &mut req, pool, world, &sender, handle_extend_selection, | ||
196 | )?; | ||
197 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | ||
198 | &mut req, pool, world, &sender, handle_document_symbol, | ||
199 | )?; | ||
200 | handle_request_on_threadpool::<req::CodeActionRequest>( | ||
201 | &mut req, pool, world, &sender, handle_code_action, | ||
202 | )?; | ||
203 | |||
204 | let mut shutdown = false; | ||
205 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | ||
206 | let resp = resp.into_response(Ok(()))?; | ||
207 | io.send(RawMsg::Response(resp)); | ||
208 | shutdown = true; | ||
209 | Ok(()) | ||
210 | })?; | ||
211 | if shutdown { | ||
212 | info!("lifecycle: initiating shutdown"); | ||
213 | drop(sender); | ||
214 | return Ok(()); | ||
215 | } | ||
216 | if let Some(req) = req { | ||
217 | error!("unknown method: {:?}", req); | ||
218 | dispatch::unknown_method(io, req)?; | ||
219 | } | ||
220 | } | ||
221 | RawMsg::Notification(not) => { | ||
222 | let mut not = Some(not); | ||
223 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | ||
224 | let path = params.text_document.file_path()?; | ||
225 | world.change_overlay(path, Some(params.text_document.text)); | ||
226 | update_file_notifications_on_threadpool( | ||
227 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | ||
228 | ); | ||
229 | Ok(()) | ||
230 | })?; | ||
231 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | ||
232 | let path = params.text_document.file_path()?; | ||
233 | let text = params.content_changes.pop() | ||
234 | .ok_or_else(|| format_err!("empty changes"))? | ||
235 | .text; | ||
236 | world.change_overlay(path, Some(text)); | ||
237 | update_file_notifications_on_threadpool( | ||
238 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | ||
239 | ); | ||
240 | Ok(()) | ||
241 | })?; | ||
242 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | ||
243 | let path = params.text_document.file_path()?; | ||
244 | world.change_overlay(path, None); | ||
245 | let not = req::PublishDiagnosticsParams { | ||
246 | uri: params.text_document.uri, | ||
247 | diagnostics: Vec::new(), | ||
248 | }; | ||
249 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); | ||
250 | io.send(RawMsg::Notification(not)); | ||
251 | Ok(()) | ||
252 | })?; | ||
253 | |||
254 | if let Some(not) = not { | ||
255 | error!("unhandled notification: {:?}", not) | ||
256 | } | ||
257 | } | ||
258 | msg => { | ||
259 | eprintln!("msg = {:?}", msg); | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | fn handle_request_on_threadpool<R: req::ClientRequest>( | ||
266 | req: &mut Option<RawRequest>, | ||
267 | pool: &ThreadPool, | ||
268 | world: &WorldState, | ||
269 | sender: &Sender<Task>, | ||
270 | f: fn(World, R::Params) -> Result<R::Result>, | ||
271 | ) -> Result<()> | ||
272 | { | ||
273 | dispatch::handle_request::<R, _>(req, |params, resp| { | ||
274 | let world = world.snapshot(); | ||
275 | let sender = sender.clone(); | ||
276 | pool.execute(move || { | ||
277 | let res = f(world, params); | ||
278 | let task = match resp.into_response(res) { | ||
279 | Ok(resp) => Task::Respond(resp), | ||
280 | Err(e) => Task::Die(e), | ||
281 | }; | ||
282 | sender.send(task); | ||
283 | }); | ||
284 | Ok(()) | ||
285 | }) | ||
286 | } | ||
287 | |||
288 | fn update_file_notifications_on_threadpool( | ||
289 | pool: &ThreadPool, | ||
290 | world: World, | ||
291 | sender: Sender<Task>, | ||
292 | uri: Url, | ||
293 | ) { | ||
294 | pool.execute(move || { | ||
295 | match publish_diagnostics(world.clone(), uri.clone()) { | ||
296 | Err(e) => { | ||
297 | error!("failed to compute diagnostics: {:?}", e) | ||
298 | } | ||
299 | Ok(params) => { | ||
300 | let not = dispatch::send_notification::<req::PublishDiagnostics>(params); | ||
301 | sender.send(Task::Notify(not)); | ||
302 | } | ||
303 | } | ||
304 | match publish_decorations(world, uri) { | ||
305 | Err(e) => { | ||
306 | error!("failed to compute decorations: {:?}", e) | ||
307 | } | ||
308 | Ok(params) => { | ||
309 | let not = dispatch::send_notification::<req::PublishDecorations>(params); | ||
310 | sender.send(Task::Notify(not)) | ||
311 | } | ||
312 | } | ||
313 | }); | ||
314 | } | ||
diff --git a/crates/server/src/handlers.rs b/crates/server/src/main_loop/handlers.rs index c6db22289..c6db22289 100644 --- a/crates/server/src/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs new file mode 100644 index 000000000..e7b24e53f --- /dev/null +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -0,0 +1,201 @@ | |||
1 | mod handlers; | ||
2 | |||
3 | use threadpool::ThreadPool; | ||
4 | use crossbeam_channel::{Sender, Receiver}; | ||
5 | use languageserver_types::Url; | ||
6 | use libanalysis::{World, WorldState}; | ||
7 | use { | ||
8 | req, dispatch, | ||
9 | Task, Result, | ||
10 | io::{Io, RawMsg, RawRequest}, | ||
11 | util::FilePath, | ||
12 | main_loop::handlers::{ | ||
13 | handle_syntax_tree, | ||
14 | handle_extend_selection, | ||
15 | publish_diagnostics, | ||
16 | publish_decorations, | ||
17 | handle_document_symbol, | ||
18 | handle_code_action, | ||
19 | }, | ||
20 | }; | ||
21 | |||
22 | pub(super) fn main_loop( | ||
23 | io: &mut Io, | ||
24 | world: &mut WorldState, | ||
25 | pool: &mut ThreadPool, | ||
26 | sender: Sender<Task>, | ||
27 | receiver: Receiver<Task>, | ||
28 | ) -> Result<()> { | ||
29 | info!("server initialized, serving requests"); | ||
30 | loop { | ||
31 | enum Event { | ||
32 | Msg(RawMsg), | ||
33 | Task(Task), | ||
34 | ReceiverDead, | ||
35 | } | ||
36 | let event = select! { | ||
37 | recv(io.receiver(), msg) => match msg { | ||
38 | Some(msg) => Event::Msg(msg), | ||
39 | None => Event::ReceiverDead, | ||
40 | }, | ||
41 | recv(receiver, task) => Event::Task(task.unwrap()), | ||
42 | }; | ||
43 | |||
44 | match event { | ||
45 | Event::ReceiverDead => { | ||
46 | io.cleanup_receiver()?; | ||
47 | unreachable!(); | ||
48 | } | ||
49 | Event::Task(task) => { | ||
50 | match task { | ||
51 | Task::Respond(response) => | ||
52 | io.send(RawMsg::Response(response)), | ||
53 | Task::Notify(n) => | ||
54 | io.send(RawMsg::Notification(n)), | ||
55 | Task::Die(error) => | ||
56 | return Err(error), | ||
57 | } | ||
58 | continue; | ||
59 | } | ||
60 | Event::Msg(msg) => { | ||
61 | if !on_msg(io, world, pool, &sender, msg)? { | ||
62 | return Ok(()); | ||
63 | } | ||
64 | } | ||
65 | }; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | fn on_msg( | ||
70 | io: &mut Io, | ||
71 | world: &mut WorldState, | ||
72 | pool: &mut ThreadPool, | ||
73 | sender: &Sender<Task>, | ||
74 | msg: RawMsg, | ||
75 | ) -> Result<bool> { | ||
76 | match msg { | ||
77 | RawMsg::Request(req) => { | ||
78 | let mut req = Some(req); | ||
79 | handle_request_on_threadpool::<req::SyntaxTree>( | ||
80 | &mut req, pool, world, sender, handle_syntax_tree, | ||
81 | )?; | ||
82 | handle_request_on_threadpool::<req::ExtendSelection>( | ||
83 | &mut req, pool, world, sender, handle_extend_selection, | ||
84 | )?; | ||
85 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | ||
86 | &mut req, pool, world, sender, handle_document_symbol, | ||
87 | )?; | ||
88 | handle_request_on_threadpool::<req::CodeActionRequest>( | ||
89 | &mut req, pool, world, sender, handle_code_action, | ||
90 | )?; | ||
91 | |||
92 | let mut shutdown = false; | ||
93 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | ||
94 | let resp = resp.into_response(Ok(()))?; | ||
95 | io.send(RawMsg::Response(resp)); | ||
96 | shutdown = true; | ||
97 | Ok(()) | ||
98 | })?; | ||
99 | if shutdown { | ||
100 | info!("lifecycle: initiating shutdown"); | ||
101 | return Ok(false); | ||
102 | } | ||
103 | if let Some(req) = req { | ||
104 | error!("unknown method: {:?}", req); | ||
105 | dispatch::unknown_method(io, req)?; | ||
106 | } | ||
107 | } | ||
108 | RawMsg::Notification(not) => { | ||
109 | let mut not = Some(not); | ||
110 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | ||
111 | let path = params.text_document.file_path()?; | ||
112 | world.change_overlay(path, Some(params.text_document.text)); | ||
113 | update_file_notifications_on_threadpool( | ||
114 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | ||
115 | ); | ||
116 | Ok(()) | ||
117 | })?; | ||
118 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | ||
119 | let path = params.text_document.file_path()?; | ||
120 | let text = params.content_changes.pop() | ||
121 | .ok_or_else(|| format_err!("empty changes"))? | ||
122 | .text; | ||
123 | world.change_overlay(path, Some(text)); | ||
124 | update_file_notifications_on_threadpool( | ||
125 | pool, world.snapshot(), sender.clone(), params.text_document.uri, | ||
126 | ); | ||
127 | Ok(()) | ||
128 | })?; | ||
129 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | ||
130 | let path = params.text_document.file_path()?; | ||
131 | world.change_overlay(path, None); | ||
132 | let not = req::PublishDiagnosticsParams { | ||
133 | uri: params.text_document.uri, | ||
134 | diagnostics: Vec::new(), | ||
135 | }; | ||
136 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); | ||
137 | io.send(RawMsg::Notification(not)); | ||
138 | Ok(()) | ||
139 | })?; | ||
140 | |||
141 | if let Some(not) = not { | ||
142 | error!("unhandled notification: {:?}", not) | ||
143 | } | ||
144 | } | ||
145 | msg => { | ||
146 | eprintln!("msg = {:?}", msg); | ||
147 | } | ||
148 | }; | ||
149 | Ok(true) | ||
150 | } | ||
151 | |||
152 | fn handle_request_on_threadpool<R: req::ClientRequest>( | ||
153 | req: &mut Option<RawRequest>, | ||
154 | pool: &ThreadPool, | ||
155 | world: &WorldState, | ||
156 | sender: &Sender<Task>, | ||
157 | f: fn(World, R::Params) -> Result<R::Result>, | ||
158 | ) -> Result<()> | ||
159 | { | ||
160 | dispatch::handle_request::<R, _>(req, |params, resp| { | ||
161 | let world = world.snapshot(); | ||
162 | let sender = sender.clone(); | ||
163 | pool.execute(move || { | ||
164 | let res = f(world, params); | ||
165 | let task = match resp.into_response(res) { | ||
166 | Ok(resp) => Task::Respond(resp), | ||
167 | Err(e) => Task::Die(e), | ||
168 | }; | ||
169 | sender.send(task); | ||
170 | }); | ||
171 | Ok(()) | ||
172 | }) | ||
173 | } | ||
174 | |||
175 | fn update_file_notifications_on_threadpool( | ||
176 | pool: &ThreadPool, | ||
177 | world: World, | ||
178 | sender: Sender<Task>, | ||
179 | uri: Url, | ||
180 | ) { | ||
181 | pool.execute(move || { | ||
182 | match publish_diagnostics(world.clone(), uri.clone()) { | ||
183 | Err(e) => { | ||
184 | error!("failed to compute diagnostics: {:?}", e) | ||
185 | } | ||
186 | Ok(params) => { | ||
187 | let not = dispatch::send_notification::<req::PublishDiagnostics>(params); | ||
188 | sender.send(Task::Notify(not)); | ||
189 | } | ||
190 | } | ||
191 | match publish_decorations(world, uri) { | ||
192 | Err(e) => { | ||
193 | error!("failed to compute decorations: {:?}", e) | ||
194 | } | ||
195 | Ok(params) => { | ||
196 | let not = dispatch::send_notification::<req::PublishDecorations>(params); | ||
197 | sender.send(Task::Notify(not)) | ||
198 | } | ||
199 | } | ||
200 | }); | ||
201 | } | ||