diff options
Diffstat (limited to 'crates/server/src/main_loop/mod.rs')
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 108 |
1 files changed, 77 insertions, 31 deletions
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index cd17cab56..db7d5ae34 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -2,12 +2,13 @@ mod handlers; | |||
2 | mod subscriptions; | 2 | mod subscriptions; |
3 | 3 | ||
4 | use std::{ | 4 | use std::{ |
5 | collections::{HashSet}, | 5 | collections::{HashMap}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use threadpool::ThreadPool; | 8 | use threadpool::ThreadPool; |
9 | use crossbeam_channel::{Sender, Receiver}; | 9 | use crossbeam_channel::{Sender, Receiver}; |
10 | use libanalysis::FileId; | 10 | use languageserver_types::{NumberOrString}; |
11 | use libanalysis::{FileId, JobHandle, JobToken}; | ||
11 | 12 | ||
12 | use { | 13 | use { |
13 | req, dispatch, | 14 | req, dispatch, |
@@ -28,7 +29,7 @@ pub(super) fn main_loop( | |||
28 | info!("server initialized, serving requests"); | 29 | info!("server initialized, serving requests"); |
29 | let mut state = ServerWorldState::new(); | 30 | let mut state = ServerWorldState::new(); |
30 | 31 | ||
31 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 32 | let mut pending_requests: HashMap<u64, JobHandle> = HashMap::new(); |
32 | let mut fs_events_receiver = Some(&fs_events_receiver); | 33 | let mut fs_events_receiver = Some(&fs_events_receiver); |
33 | let mut subs = Subscriptions::new(); | 34 | let mut subs = Subscriptions::new(); |
34 | loop { | 35 | loop { |
@@ -61,8 +62,12 @@ pub(super) fn main_loop( | |||
61 | } | 62 | } |
62 | Event::Task(task) => { | 63 | Event::Task(task) => { |
63 | match task { | 64 | match task { |
64 | Task::Respond(response) => | 65 | Task::Respond(response) => { |
65 | io.send(RawMsg::Response(response)), | 66 | if let Some(handle) = pending_requests.remove(&response.id) { |
67 | assert!(handle.has_completed()); | ||
68 | } | ||
69 | io.send(RawMsg::Response(response)) | ||
70 | } | ||
66 | Task::Notify(n) => | 71 | Task::Notify(n) => |
67 | io.send(RawMsg::Notification(n)), | 72 | io.send(RawMsg::Notification(n)), |
68 | Task::Die(error) => | 73 | Task::Die(error) => |
@@ -78,18 +83,16 @@ pub(super) fn main_loop( | |||
78 | Event::Msg(msg) => { | 83 | Event::Msg(msg) => { |
79 | match msg { | 84 | match msg { |
80 | RawMsg::Request(req) => { | 85 | RawMsg::Request(req) => { |
81 | if !on_request(io, &mut state, pool, &task_sender, req)? { | 86 | if !on_request(io, &mut state, &mut pending_requests, pool, &task_sender, req)? { |
82 | return Ok(()); | 87 | return Ok(()); |
83 | } | 88 | } |
84 | } | 89 | } |
85 | RawMsg::Notification(not) => { | 90 | RawMsg::Notification(not) => { |
86 | on_notification(io, &mut state, &mut subs, not)?; | 91 | on_notification(io, &mut state, &mut pending_requests, &mut subs, not)?; |
87 | state_changed = true; | 92 | state_changed = true; |
88 | } | 93 | } |
89 | RawMsg::Response(resp) => { | 94 | RawMsg::Response(resp) => { |
90 | if !pending_requests.remove(&resp.id) { | 95 | error!("unexpected response: {:?}", resp) |
91 | error!("unexpected response: {:?}", resp) | ||
92 | } | ||
93 | } | 96 | } |
94 | } | 97 | } |
95 | } | 98 | } |
@@ -109,15 +112,17 @@ pub(super) fn main_loop( | |||
109 | fn on_request( | 112 | fn on_request( |
110 | io: &mut Io, | 113 | io: &mut Io, |
111 | world: &mut ServerWorldState, | 114 | world: &mut ServerWorldState, |
115 | pending_requests: &mut HashMap<u64, JobHandle>, | ||
112 | pool: &ThreadPool, | 116 | pool: &ThreadPool, |
113 | sender: &Sender<Task>, | 117 | sender: &Sender<Task>, |
114 | req: RawRequest, | 118 | req: RawRequest, |
115 | ) -> Result<bool> { | 119 | ) -> Result<bool> { |
116 | let mut pool_dispatcher = PoolDispatcher { | 120 | let mut pool_dispatcher = PoolDispatcher { |
117 | req: Some(req), | 121 | req: Some(req), |
122 | res: None, | ||
118 | pool, world, sender | 123 | pool, world, sender |
119 | }; | 124 | }; |
120 | pool_dispatcher | 125 | let req = pool_dispatcher |
121 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? | 126 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
122 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? | 127 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? |
123 | .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? | 128 | .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? |
@@ -130,23 +135,30 @@ fn on_request( | |||
130 | .on::<req::Runnables>(handlers::handle_runnables)? | 135 | .on::<req::Runnables>(handlers::handle_runnables)? |
131 | .on::<req::DecorationsRequest>(handlers::handle_decorations)? | 136 | .on::<req::DecorationsRequest>(handlers::handle_decorations)? |
132 | .on::<req::Completion>(handlers::handle_completion)? | 137 | .on::<req::Completion>(handlers::handle_completion)? |
133 | .on::<req::CodeActionRequest>(handlers::handle_code_action)?; | 138 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? |
134 | 139 | .finish(); | |
135 | let mut req = pool_dispatcher.req; | 140 | match req { |
136 | let mut shutdown = false; | 141 | Ok((id, handle)) => { |
137 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | 142 | let inserted = pending_requests.insert(id, handle).is_none(); |
138 | let resp = resp.into_response(Ok(()))?; | 143 | assert!(inserted, "duplicate request: {}", id); |
139 | io.send(RawMsg::Response(resp)); | 144 | }, |
140 | shutdown = true; | 145 | Err(req) => { |
141 | Ok(()) | 146 | let req = dispatch::handle_request::<req::Shutdown, _>(req, |(), resp| { |
142 | })?; | 147 | let resp = resp.into_response(Ok(()))?; |
143 | if shutdown { | 148 | io.send(RawMsg::Response(resp)); |
144 | info!("lifecycle: initiating shutdown"); | 149 | Ok(()) |
145 | return Ok(false); | 150 | })?; |
146 | } | 151 | match req { |
147 | if let Some(req) = req { | 152 | Ok(_id) => { |
148 | error!("unknown method: {:?}", req); | 153 | info!("lifecycle: initiating shutdown"); |
149 | io.send(RawMsg::Response(dispatch::unknown_method(req.id)?)); | 154 | return Ok(false); |
155 | } | ||
156 | Err(req) => { | ||
157 | error!("unknown method: {:?}", req); | ||
158 | io.send(RawMsg::Response(dispatch::unknown_method(req.id)?)); | ||
159 | } | ||
160 | } | ||
161 | } | ||
150 | } | 162 | } |
151 | Ok(true) | 163 | Ok(true) |
152 | } | 164 | } |
@@ -154,10 +166,23 @@ fn on_request( | |||
154 | fn on_notification( | 166 | fn on_notification( |
155 | io: &mut Io, | 167 | io: &mut Io, |
156 | state: &mut ServerWorldState, | 168 | state: &mut ServerWorldState, |
169 | pending_requests: &mut HashMap<u64, JobHandle>, | ||
157 | subs: &mut Subscriptions, | 170 | subs: &mut Subscriptions, |
158 | not: RawNotification, | 171 | not: RawNotification, |
159 | ) -> Result<()> { | 172 | ) -> Result<()> { |
160 | let mut not = Some(not); | 173 | let mut not = Some(not); |
174 | dispatch::handle_notification::<req::Cancel, _>(&mut not, |params| { | ||
175 | let id = match params.id { | ||
176 | NumberOrString::Number(id) => id, | ||
177 | NumberOrString::String(id) => { | ||
178 | panic!("string id's not supported: {:?}", id); | ||
179 | } | ||
180 | }; | ||
181 | if let Some(handle) = pending_requests.remove(&id) { | ||
182 | handle.cancel(); | ||
183 | } | ||
184 | Ok(()) | ||
185 | })?; | ||
161 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | 186 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { |
162 | let uri = params.text_document.uri; | 187 | let uri = params.text_document.uri; |
163 | let path = uri.to_file_path() | 188 | let path = uri.to_file_path() |
@@ -196,21 +221,30 @@ fn on_notification( | |||
196 | 221 | ||
197 | struct PoolDispatcher<'a> { | 222 | struct PoolDispatcher<'a> { |
198 | req: Option<RawRequest>, | 223 | req: Option<RawRequest>, |
224 | res: Option<(u64, JobHandle)>, | ||
199 | pool: &'a ThreadPool, | 225 | pool: &'a ThreadPool, |
200 | world: &'a ServerWorldState, | 226 | world: &'a ServerWorldState, |
201 | sender: &'a Sender<Task>, | 227 | sender: &'a Sender<Task>, |
202 | } | 228 | } |
203 | 229 | ||
204 | impl<'a> PoolDispatcher<'a> { | 230 | impl<'a> PoolDispatcher<'a> { |
205 | fn on<'b, R: req::ClientRequest>(&'b mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&'b mut Self> { | 231 | fn on<'b, R: req::ClientRequest>( |
232 | &'b mut self, | ||
233 | f: fn(ServerWorld, R::Params, JobToken) -> Result<R::Result> | ||
234 | ) -> Result<&'b mut Self> { | ||
235 | let req = match self.req.take() { | ||
236 | None => return Ok(self), | ||
237 | Some(req) => req, | ||
238 | }; | ||
206 | let world = self.world; | 239 | let world = self.world; |
207 | let sender = self.sender; | 240 | let sender = self.sender; |
208 | let pool = self.pool; | 241 | let pool = self.pool; |
209 | dispatch::handle_request::<R, _>(&mut self.req, |params, resp| { | 242 | let (handle, token) = JobHandle::new(); |
243 | let req = dispatch::handle_request::<R, _>(req, |params, resp| { | ||
210 | let world = world.snapshot(); | 244 | let world = world.snapshot(); |
211 | let sender = sender.clone(); | 245 | let sender = sender.clone(); |
212 | pool.execute(move || { | 246 | pool.execute(move || { |
213 | let res = f(world, params); | 247 | let res = f(world, params, token); |
214 | let task = match resp.into_response(res) { | 248 | let task = match resp.into_response(res) { |
215 | Ok(resp) => Task::Respond(resp), | 249 | Ok(resp) => Task::Respond(resp), |
216 | Err(e) => Task::Die(e), | 250 | Err(e) => Task::Die(e), |
@@ -219,8 +253,20 @@ impl<'a> PoolDispatcher<'a> { | |||
219 | }); | 253 | }); |
220 | Ok(()) | 254 | Ok(()) |
221 | })?; | 255 | })?; |
256 | match req { | ||
257 | Ok(id) => self.res = Some((id, handle)), | ||
258 | Err(req) => self.req = Some(req), | ||
259 | } | ||
222 | Ok(self) | 260 | Ok(self) |
223 | } | 261 | } |
262 | |||
263 | fn finish(&mut self) -> ::std::result::Result<(u64, JobHandle), RawRequest> { | ||
264 | match (self.res.take(), self.req.take()) { | ||
265 | (Some(res), None) => Ok(res), | ||
266 | (None, Some(req)) => Err(req), | ||
267 | _ => unreachable!(), | ||
268 | } | ||
269 | } | ||
224 | } | 270 | } |
225 | 271 | ||
226 | fn update_file_notifications_on_threadpool( | 272 | fn update_file_notifications_on_threadpool( |