diff options
Diffstat (limited to 'crates/server/src/main_loop/mod.rs')
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 125 |
1 files changed, 40 insertions, 85 deletions
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index a8340df59..ad7c480dc 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -1,21 +1,20 @@ | |||
1 | mod handlers; | 1 | mod handlers; |
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | collections::{HashSet, HashMap}, | 4 | collections::{HashSet}, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use threadpool::ThreadPool; | 7 | use threadpool::ThreadPool; |
8 | use crossbeam_channel::{Sender, Receiver}; | 8 | use crossbeam_channel::{Sender, Receiver}; |
9 | use languageserver_types::Url; | 9 | use languageserver_types::Url; |
10 | use libanalysis::{World, WorldState, FileId}; | ||
11 | use serde_json::to_value; | 10 | use serde_json::to_value; |
12 | 11 | ||
13 | use { | 12 | use { |
14 | req, dispatch, | 13 | req, dispatch, |
15 | Task, Result, PathMap, | 14 | Task, Result, |
16 | io::{Io, RawMsg, RawRequest, RawNotification}, | 15 | io::{Io, RawMsg, RawRequest, RawNotification}, |
17 | vfs::{FileEvent, FileEventKind}, | 16 | vfs::FileEvent, |
18 | conv::TryConvWith, | 17 | server_world::{ServerWorldState, ServerWorld}, |
19 | main_loop::handlers::{ | 18 | main_loop::handlers::{ |
20 | handle_syntax_tree, | 19 | handle_syntax_tree, |
21 | handle_extend_selection, | 20 | handle_extend_selection, |
@@ -32,17 +31,16 @@ use { | |||
32 | 31 | ||
33 | pub(super) fn main_loop( | 32 | pub(super) fn main_loop( |
34 | io: &mut Io, | 33 | io: &mut Io, |
35 | world: &mut WorldState, | ||
36 | pool: &mut ThreadPool, | 34 | pool: &mut ThreadPool, |
37 | task_sender: Sender<Task>, | 35 | task_sender: Sender<Task>, |
38 | task_receiver: Receiver<Task>, | 36 | task_receiver: Receiver<Task>, |
39 | fs_events_receiver: Receiver<Vec<FileEvent>>, | 37 | fs_events_receiver: Receiver<Vec<FileEvent>>, |
40 | ) -> Result<()> { | 38 | ) -> Result<()> { |
41 | info!("server initialized, serving requests"); | 39 | info!("server initialized, serving requests"); |
40 | let mut state = ServerWorldState::new(); | ||
41 | |||
42 | let mut next_request_id = 0; | 42 | let mut next_request_id = 0; |
43 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 43 | let mut pending_requests: HashSet<u64> = HashSet::new(); |
44 | let mut path_map = PathMap::new(); | ||
45 | let mut mem_map: HashMap<FileId, Option<String>> = HashMap::new(); | ||
46 | let mut fs_events_receiver = Some(&fs_events_receiver); | 44 | let mut fs_events_receiver = Some(&fs_events_receiver); |
47 | loop { | 45 | loop { |
48 | enum Event { | 46 | enum Event { |
@@ -91,37 +89,17 @@ pub(super) fn main_loop( | |||
91 | } | 89 | } |
92 | Event::Fs(events) => { | 90 | Event::Fs(events) => { |
93 | trace!("fs change, {} events", events.len()); | 91 | trace!("fs change, {} events", events.len()); |
94 | let changes = events.into_iter() | 92 | state.apply_fs_changes(events); |
95 | .map(|event| { | ||
96 | let text = match event.kind { | ||
97 | FileEventKind::Add(text) => Some(text), | ||
98 | FileEventKind::Remove => None, | ||
99 | }; | ||
100 | (event.path, text) | ||
101 | }) | ||
102 | .map(|(path, text)| { | ||
103 | (path_map.get_or_insert(path), text) | ||
104 | }) | ||
105 | .filter_map(|(id, text)| { | ||
106 | if mem_map.contains_key(&id) { | ||
107 | mem_map.insert(id, text); | ||
108 | None | ||
109 | } else { | ||
110 | Some((id, text)) | ||
111 | } | ||
112 | }); | ||
113 | |||
114 | world.change_files(changes); | ||
115 | } | 93 | } |
116 | Event::Msg(msg) => { | 94 | Event::Msg(msg) => { |
117 | match msg { | 95 | match msg { |
118 | RawMsg::Request(req) => { | 96 | RawMsg::Request(req) => { |
119 | if !on_request(io, world, &path_map, pool, &task_sender, req)? { | 97 | if !on_request(io, &state, pool, &task_sender, req)? { |
120 | return Ok(()); | 98 | return Ok(()); |
121 | } | 99 | } |
122 | } | 100 | } |
123 | RawMsg::Notification(not) => { | 101 | RawMsg::Notification(not) => { |
124 | on_notification(io, world, &mut path_map, pool, &task_sender, not, &mut mem_map)? | 102 | on_notification(io, &mut state, pool, &task_sender, not)? |
125 | } | 103 | } |
126 | RawMsg::Response(resp) => { | 104 | RawMsg::Response(resp) => { |
127 | if !pending_requests.remove(&resp.id) { | 105 | if !pending_requests.remove(&resp.id) { |
@@ -136,45 +114,40 @@ pub(super) fn main_loop( | |||
136 | 114 | ||
137 | fn on_request( | 115 | fn on_request( |
138 | io: &mut Io, | 116 | io: &mut Io, |
139 | world: &WorldState, | 117 | world: &ServerWorldState, |
140 | path_map: &PathMap, | ||
141 | pool: &ThreadPool, | 118 | pool: &ThreadPool, |
142 | sender: &Sender<Task>, | 119 | sender: &Sender<Task>, |
143 | req: RawRequest, | 120 | req: RawRequest, |
144 | ) -> Result<bool> { | 121 | ) -> Result<bool> { |
145 | let mut req = Some(req); | 122 | let mut req = Some(req); |
146 | handle_request_on_threadpool::<req::SyntaxTree>( | 123 | handle_request_on_threadpool::<req::SyntaxTree>( |
147 | &mut req, pool, path_map, world, sender, handle_syntax_tree, | 124 | &mut req, pool, world, sender, handle_syntax_tree, |
148 | )?; | 125 | )?; |
149 | handle_request_on_threadpool::<req::ExtendSelection>( | 126 | handle_request_on_threadpool::<req::ExtendSelection>( |
150 | &mut req, pool, path_map, world, sender, handle_extend_selection, | 127 | &mut req, pool, world, sender, handle_extend_selection, |
151 | )?; | 128 | )?; |
152 | handle_request_on_threadpool::<req::FindMatchingBrace>( | 129 | handle_request_on_threadpool::<req::FindMatchingBrace>( |
153 | &mut req, pool, path_map, world, sender, handle_find_matching_brace, | 130 | &mut req, pool, world, sender, handle_find_matching_brace, |
154 | )?; | 131 | )?; |
155 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 132 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( |
156 | &mut req, pool, path_map, world, sender, handle_document_symbol, | 133 | &mut req, pool, world, sender, handle_document_symbol, |
157 | )?; | 134 | )?; |
158 | handle_request_on_threadpool::<req::CodeActionRequest>( | 135 | handle_request_on_threadpool::<req::CodeActionRequest>( |
159 | &mut req, pool, path_map, world, sender, handle_code_action, | 136 | &mut req, pool, world, sender, handle_code_action, |
160 | )?; | 137 | )?; |
161 | handle_request_on_threadpool::<req::WorkspaceSymbol>( | 138 | handle_request_on_threadpool::<req::WorkspaceSymbol>( |
162 | &mut req, pool, path_map, world, sender, handle_workspace_symbol, | 139 | &mut req, pool, world, sender, handle_workspace_symbol, |
163 | )?; | 140 | )?; |
164 | handle_request_on_threadpool::<req::GotoDefinition>( | 141 | handle_request_on_threadpool::<req::GotoDefinition>( |
165 | &mut req, pool, path_map, world, sender, handle_goto_definition, | 142 | &mut req, pool, world, sender, handle_goto_definition, |
166 | )?; | 143 | )?; |
167 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | 144 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { |
168 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | 145 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); |
169 | 146 | ||
170 | let world = world.snapshot({ | 147 | let world = world.snapshot(); |
171 | let pm = path_map.clone(); | ||
172 | move |id, path| pm.resolve(id, path) | ||
173 | }); | ||
174 | let path_map = path_map.clone(); | ||
175 | let sender = sender.clone(); | 148 | let sender = sender.clone(); |
176 | pool.execute(move || { | 149 | pool.execute(move || { |
177 | let (edit, cursor) = match handle_execute_command(world, path_map, params) { | 150 | let (edit, cursor) = match handle_execute_command(world, params) { |
178 | Ok(res) => res, | 151 | Ok(res) => res, |
179 | Err(e) => return sender.send(Task::Die(e)), | 152 | Err(e) => return sender.send(Task::Die(e)), |
180 | }; | 153 | }; |
@@ -221,60 +194,48 @@ fn on_request( | |||
221 | 194 | ||
222 | fn on_notification( | 195 | fn on_notification( |
223 | io: &mut Io, | 196 | io: &mut Io, |
224 | world: &mut WorldState, | 197 | state: &mut ServerWorldState, |
225 | path_map: &mut PathMap, | ||
226 | pool: &ThreadPool, | 198 | pool: &ThreadPool, |
227 | sender: &Sender<Task>, | 199 | sender: &Sender<Task>, |
228 | not: RawNotification, | 200 | not: RawNotification, |
229 | mem_map: &mut HashMap<FileId, Option<String>>, | ||
230 | ) -> Result<()> { | 201 | ) -> Result<()> { |
231 | let mut not = Some(not); | 202 | let mut not = Some(not); |
232 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | 203 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { |
233 | let uri = params.text_document.uri; | 204 | let uri = params.text_document.uri; |
234 | let path = uri.to_file_path() | 205 | let path = uri.to_file_path() |
235 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | 206 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
236 | let file_id = path_map.get_or_insert(path); | 207 | state.add_mem_file(path, params.text_document.text); |
237 | mem_map.insert(file_id, None); | ||
238 | world.change_file(file_id, Some(params.text_document.text)); | ||
239 | update_file_notifications_on_threadpool( | 208 | update_file_notifications_on_threadpool( |
240 | pool, | 209 | pool, |
241 | world.snapshot({ | 210 | state.snapshot(), |
242 | let pm = path_map.clone(); | ||
243 | move |id, path| pm.resolve(id, path) | ||
244 | }), | ||
245 | path_map.clone(), | ||
246 | sender.clone(), | 211 | sender.clone(), |
247 | uri, | 212 | uri, |
248 | ); | 213 | ); |
249 | Ok(()) | 214 | Ok(()) |
250 | })?; | 215 | })?; |
251 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | 216 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { |
252 | let file_id = params.text_document.try_conv_with(path_map)?; | 217 | let uri = params.text_document.uri; |
218 | let path = uri.to_file_path() | ||
219 | .map_err(|()| format_err!("invalid uri: {}", uri))?; | ||
253 | let text = params.content_changes.pop() | 220 | let text = params.content_changes.pop() |
254 | .ok_or_else(|| format_err!("empty changes"))? | 221 | .ok_or_else(|| format_err!("empty changes"))? |
255 | .text; | 222 | .text; |
256 | world.change_file(file_id, Some(text)); | 223 | state.change_mem_file(path.as_path(), text)?; |
257 | update_file_notifications_on_threadpool( | 224 | update_file_notifications_on_threadpool( |
258 | pool, | 225 | pool, |
259 | world.snapshot({ | 226 | state.snapshot(), |
260 | let pm = path_map.clone(); | ||
261 | move |id, path| pm.resolve(id, path) | ||
262 | }), | ||
263 | path_map.clone(), | ||
264 | sender.clone(), | 227 | sender.clone(), |
265 | params.text_document.uri, | 228 | uri, |
266 | ); | 229 | ); |
267 | Ok(()) | 230 | Ok(()) |
268 | })?; | 231 | })?; |
269 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | 232 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { |
270 | let file_id = params.text_document.try_conv_with(path_map)?; | 233 | let uri = params.text_document.uri; |
271 | let text = match mem_map.remove(&file_id) { | 234 | let path = uri.to_file_path() |
272 | Some(text) => text, | 235 | .map_err(|()| format_err!("invalid uri: {}", uri))?; |
273 | None => bail!("unmatched close notification"), | 236 | state.remove_mem_file(path.as_path())?; |
274 | }; | ||
275 | world.change_file(file_id, text); | ||
276 | let not = req::PublishDiagnosticsParams { | 237 | let not = req::PublishDiagnosticsParams { |
277 | uri: params.text_document.uri, | 238 | uri, |
278 | diagnostics: Vec::new(), | 239 | diagnostics: Vec::new(), |
279 | }; | 240 | }; |
280 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); | 241 | let not = dispatch::send_notification::<req::PublishDiagnostics>(not); |
@@ -291,21 +252,16 @@ fn on_notification( | |||
291 | fn handle_request_on_threadpool<R: req::ClientRequest>( | 252 | fn handle_request_on_threadpool<R: req::ClientRequest>( |
292 | req: &mut Option<RawRequest>, | 253 | req: &mut Option<RawRequest>, |
293 | pool: &ThreadPool, | 254 | pool: &ThreadPool, |
294 | path_map: &PathMap, | 255 | world: &ServerWorldState, |
295 | world: &WorldState, | ||
296 | sender: &Sender<Task>, | 256 | sender: &Sender<Task>, |
297 | f: fn(World, PathMap, R::Params) -> Result<R::Result>, | 257 | f: fn(ServerWorld, R::Params) -> Result<R::Result>, |
298 | ) -> Result<()> | 258 | ) -> Result<()> |
299 | { | 259 | { |
300 | dispatch::handle_request::<R, _>(req, |params, resp| { | 260 | dispatch::handle_request::<R, _>(req, |params, resp| { |
301 | let world = world.snapshot({ | 261 | let world = world.snapshot(); |
302 | let pm = path_map.clone(); | ||
303 | move |id, path| pm.resolve(id, path) | ||
304 | }); | ||
305 | let path_map = path_map.clone(); | ||
306 | let sender = sender.clone(); | 262 | let sender = sender.clone(); |
307 | pool.execute(move || { | 263 | pool.execute(move || { |
308 | let res = f(world, path_map, params); | 264 | let res = f(world, params); |
309 | let task = match resp.into_response(res) { | 265 | let task = match resp.into_response(res) { |
310 | Ok(resp) => Task::Respond(resp), | 266 | Ok(resp) => Task::Respond(resp), |
311 | Err(e) => Task::Die(e), | 267 | Err(e) => Task::Die(e), |
@@ -318,13 +274,12 @@ fn handle_request_on_threadpool<R: req::ClientRequest>( | |||
318 | 274 | ||
319 | fn update_file_notifications_on_threadpool( | 275 | fn update_file_notifications_on_threadpool( |
320 | pool: &ThreadPool, | 276 | pool: &ThreadPool, |
321 | world: World, | 277 | world: ServerWorld, |
322 | path_map: PathMap, | ||
323 | sender: Sender<Task>, | 278 | sender: Sender<Task>, |
324 | uri: Url, | 279 | uri: Url, |
325 | ) { | 280 | ) { |
326 | pool.execute(move || { | 281 | pool.execute(move || { |
327 | match publish_diagnostics(world.clone(), path_map.clone(), uri.clone()) { | 282 | match publish_diagnostics(world.clone(), uri.clone()) { |
328 | Err(e) => { | 283 | Err(e) => { |
329 | error!("failed to compute diagnostics: {:?}", e) | 284 | error!("failed to compute diagnostics: {:?}", e) |
330 | } | 285 | } |
@@ -333,7 +288,7 @@ fn update_file_notifications_on_threadpool( | |||
333 | sender.send(Task::Notify(not)); | 288 | sender.send(Task::Notify(not)); |
334 | } | 289 | } |
335 | } | 290 | } |
336 | match publish_decorations(world, path_map.clone(), uri) { | 291 | match publish_decorations(world, uri) { |
337 | Err(e) => { | 292 | Err(e) => { |
338 | error!("failed to compute decorations: {:?}", e) | 293 | error!("failed to compute decorations: {:?}", e) |
339 | } | 294 | } |