aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/server/src/main.rs184
-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.rs201
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;
21mod caps; 21mod caps;
22mod req; 22mod req;
23mod dispatch; 23mod dispatch;
24mod handlers;
25mod util; 24mod util;
26mod conv; 25mod conv;
26mod main_loop;
27 27
28use threadpool::ThreadPool; 28use threadpool::ThreadPool;
29use crossbeam_channel::{bounded, Sender, Receiver}; 29use crossbeam_channel::bounded;
30use flexi_logger::Logger; 30use flexi_logger::Logger;
31use languageserver_types::Url; 31use libanalysis::WorldState;
32use libanalysis::{WorldState, World};
33 32
34use ::{ 33use ::{
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
41pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 37pub 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
146fn 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
265fn 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
288fn 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 @@
1mod handlers;
2
3use threadpool::ThreadPool;
4use crossbeam_channel::{Sender, Receiver};
5use languageserver_types::Url;
6use libanalysis::{World, WorldState};
7use {
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
22pub(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
69fn 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
152fn 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
175fn 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}