aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-05-31 19:50:34 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-05-31 19:50:34 +0100
commit5023860a55328db5ef024225263ed71f46a19350 (patch)
tree0ea40483691d7b9a7822149c670aeb84231a1bb2 /crates/ra_lsp_server
parent6c1ec865fb931ffd789e2caa9e2ce82d1df97c4d (diff)
parent78e17f65cfa0013ea51d94f0142ca6d4bcc5d088 (diff)
Merge #1356
1356: move completed requests to a separate file r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs322
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs8
-rw-r--r--crates/ra_lsp_server/src/main_loop/pending_requests.rs72
-rw-r--r--crates/ra_lsp_server/src/main_loop/subscriptions.rs12
-rw-r--r--crates/ra_lsp_server/src/server_world.rs36
5 files changed, 263 insertions, 187 deletions
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index d29ba94e7..452499497 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -1,7 +1,8 @@
1mod handlers; 1mod handlers;
2mod subscriptions; 2mod subscriptions;
3pub(crate) mod pending_requests;
3 4
4use std::{fmt, path::PathBuf, sync::Arc, time::Instant, any::TypeId}; 5use std::{fmt, path::PathBuf, sync::Arc, time::Instant};
5 6
6use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; 7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender};
7use failure::{bail, format_err}; 8use failure::{bail, format_err};
@@ -12,19 +13,24 @@ use gen_lsp_server::{
12use lsp_types::NumberOrString; 13use lsp_types::NumberOrString;
13use ra_ide_api::{Canceled, FileId, LibraryData}; 14use ra_ide_api::{Canceled, FileId, LibraryData};
14use ra_vfs::VfsTask; 15use ra_vfs::VfsTask;
15use rustc_hash::FxHashMap;
16use serde::{de::DeserializeOwned, Serialize}; 16use serde::{de::DeserializeOwned, Serialize};
17use threadpool::ThreadPool; 17use threadpool::ThreadPool;
18use ra_prof::profile;
18 19
19use crate::{ 20use crate::{
20 main_loop::subscriptions::Subscriptions, 21 main_loop::{
22 subscriptions::Subscriptions,
23 pending_requests::{PendingRequests, PendingRequest},
24 },
21 project_model::workspace_loader, 25 project_model::workspace_loader,
22 req, 26 req,
23 server_world::{ServerWorld, ServerWorldState, CompletedRequest}, 27 server_world::{ServerWorld, ServerWorldState},
24 Result, 28 Result,
25 InitializationOptions, 29 InitializationOptions,
26}; 30};
27use ra_prof::profile; 31
32const THREADPOOL_SIZE: usize = 8;
33const MAX_IN_FLIGHT_LIBS: usize = THREADPOOL_SIZE - 3;
28 34
29#[derive(Debug, Fail)] 35#[derive(Debug, Fail)]
30#[fail(display = "Language Server request failed with {}. ({})", code, message)] 36#[fail(display = "Language Server request failed with {}. ({})", code, message)]
@@ -39,34 +45,12 @@ impl LspError {
39 } 45 }
40} 46}
41 47
42#[derive(Debug)]
43enum Task {
44 Respond(RawResponse),
45 Notify(RawNotification),
46}
47
48struct PendingRequest {
49 received: Instant,
50 method: String,
51}
52
53impl From<(u64, PendingRequest)> for CompletedRequest {
54 fn from((id, pending): (u64, PendingRequest)) -> CompletedRequest {
55 CompletedRequest { id, method: pending.method, duration: pending.received.elapsed() }
56 }
57}
58
59const THREADPOOL_SIZE: usize = 8;
60
61pub fn main_loop( 48pub fn main_loop(
62 ws_roots: Vec<PathBuf>, 49 ws_roots: Vec<PathBuf>,
63 options: InitializationOptions, 50 options: InitializationOptions,
64 msg_receiver: &Receiver<RawMessage>, 51 msg_receiver: &Receiver<RawMessage>,
65 msg_sender: &Sender<RawMessage>, 52 msg_sender: &Sender<RawMessage>,
66) -> Result<()> { 53) -> Result<()> {
67 let pool = ThreadPool::new(THREADPOOL_SIZE);
68 let (task_sender, task_receiver) = unbounded::<Task>();
69
70 // FIXME: support dynamic workspace loading. 54 // FIXME: support dynamic workspace loading.
71 let workspaces = { 55 let workspaces = {
72 let ws_worker = workspace_loader(); 56 let ws_worker = workspace_loader();
@@ -91,10 +75,12 @@ pub fn main_loop(
91 75
92 let mut state = ServerWorldState::new(ws_roots, workspaces); 76 let mut state = ServerWorldState::new(ws_roots, workspaces);
93 77
94 log::info!("server initialized, serving requests"); 78 let pool = ThreadPool::new(THREADPOOL_SIZE);
79 let (task_sender, task_receiver) = unbounded::<Task>();
80 let mut pending_requests = PendingRequests::default();
81 let mut subs = Subscriptions::default();
95 82
96 let mut pending_requests = FxHashMap::default(); 83 log::info!("server initialized, serving requests");
97 let mut subs = Subscriptions::new();
98 let main_res = main_loop_inner( 84 let main_res = main_loop_inner(
99 options, 85 options,
100 &pool, 86 &pool,
@@ -122,6 +108,12 @@ pub fn main_loop(
122 main_res 108 main_res
123} 109}
124 110
111#[derive(Debug)]
112enum Task {
113 Respond(RawResponse),
114 Notify(RawNotification),
115}
116
125enum Event { 117enum Event {
126 Msg(RawMessage), 118 Msg(RawMessage),
127 Task(Task), 119 Task(Task),
@@ -172,10 +164,10 @@ fn main_loop_inner(
172 task_sender: Sender<Task>, 164 task_sender: Sender<Task>,
173 task_receiver: Receiver<Task>, 165 task_receiver: Receiver<Task>,
174 state: &mut ServerWorldState, 166 state: &mut ServerWorldState,
175 pending_requests: &mut FxHashMap<u64, PendingRequest>, 167 pending_requests: &mut PendingRequests,
176 subs: &mut Subscriptions, 168 subs: &mut Subscriptions,
177) -> Result<()> { 169) -> Result<()> {
178 // We try not to index more than THREADPOOL_SIZE - 3 libraries at the same 170 // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
179 // time to always have a thread ready to react to input. 171 // time to always have a thread ready to react to input.
180 let mut in_flight_libraries = 0; 172 let mut in_flight_libraries = 0;
181 let mut pending_libraries = Vec::new(); 173 let mut pending_libraries = Vec::new();
@@ -196,15 +188,16 @@ fn main_loop_inner(
196 }, 188 },
197 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) 189 recv(libdata_receiver) -> data => Event::Lib(data.unwrap())
198 }; 190 };
199 // NOTE: don't count blocking select! call as a loop-turn time
200 let _p = profile("main_loop_inner/loop-turn");
201 let loop_start = Instant::now(); 191 let loop_start = Instant::now();
202 192
193 // NOTE: don't count blocking select! call as a loop-turn time
194 let _p = profile("main_loop_inner/loop-turn");
203 log::info!("loop turn = {:?}", event); 195 log::info!("loop turn = {:?}", event);
204 let queue_count = pool.queued_count(); 196 let queue_count = pool.queued_count();
205 if queue_count > 0 { 197 if queue_count > 0 {
206 log::info!("queued count = {}", queue_count); 198 log::info!("queued count = {}", queue_count);
207 } 199 }
200
208 let mut state_changed = false; 201 let mut state_changed = false;
209 match event { 202 match event {
210 Event::Task(task) => { 203 Event::Task(task) => {
@@ -226,34 +219,15 @@ fn main_loop_inner(
226 Some(req) => req, 219 Some(req) => req,
227 None => return Ok(()), 220 None => return Ok(()),
228 }; 221 };
229 match req.cast::<req::CollectGarbage>() { 222 on_request(
230 Ok((id, ())) => { 223 state,
231 state.collect_garbage(); 224 pending_requests,
232 let resp = RawResponse::ok::<req::CollectGarbage>(id, &()); 225 pool,
233 msg_sender.send(resp.into()).unwrap() 226 &task_sender,
234 } 227 msg_sender,
235 Err(req) => { 228 loop_start,
236 match on_request( 229 req,
237 state, 230 )?
238 pending_requests,
239 pool,
240 &task_sender,
241 loop_start,
242 req,
243 )? {
244 None => (),
245 Some(req) => {
246 log::error!("unknown request: {:?}", req);
247 let resp = RawResponse::err(
248 req.id,
249 ErrorCode::MethodNotFound as i32,
250 "unknown request".to_string(),
251 );
252 msg_sender.send(resp.into()).unwrap()
253 }
254 }
255 }
256 }
257 } 231 }
258 RawMessage::Notification(not) => { 232 RawMessage::Notification(not) => {
259 on_notification(msg_sender, state, pending_requests, subs, not)?; 233 on_notification(msg_sender, state, pending_requests, subs, not)?;
@@ -264,7 +238,7 @@ fn main_loop_inner(
264 }; 238 };
265 239
266 pending_libraries.extend(state.process_changes()); 240 pending_libraries.extend(state.process_changes());
267 while in_flight_libraries < THREADPOOL_SIZE - 3 && !pending_libraries.is_empty() { 241 while in_flight_libraries < MAX_IN_FLIGHT_LIBS && !pending_libraries.is_empty() {
268 let (root, files) = pending_libraries.pop().unwrap(); 242 let (root, files) = pending_libraries.pop().unwrap();
269 in_flight_libraries += 1; 243 in_flight_libraries += 1;
270 let sender = libdata_sender.clone(); 244 let sender = libdata_sender.clone();
@@ -305,13 +279,12 @@ fn main_loop_inner(
305fn on_task( 279fn on_task(
306 task: Task, 280 task: Task,
307 msg_sender: &Sender<RawMessage>, 281 msg_sender: &Sender<RawMessage>,
308 pending_requests: &mut FxHashMap<u64, PendingRequest>, 282 pending_requests: &mut PendingRequests,
309 state: &mut ServerWorldState, 283 state: &mut ServerWorldState,
310) { 284) {
311 match task { 285 match task {
312 Task::Respond(response) => { 286 Task::Respond(response) => {
313 if let Some(pending) = pending_requests.remove(&response.id) { 287 if let Some(completed) = pending_requests.finish(response.id) {
314 let completed = CompletedRequest::from((response.id, pending));
315 log::info!("handled req#{} in {:?}", completed.id, completed.duration); 288 log::info!("handled req#{} in {:?}", completed.id, completed.duration);
316 state.complete_request(completed); 289 state.complete_request(completed);
317 msg_sender.send(response.into()).unwrap(); 290 msg_sender.send(response.into()).unwrap();
@@ -325,22 +298,35 @@ fn on_task(
325 298
326fn on_request( 299fn on_request(
327 world: &mut ServerWorldState, 300 world: &mut ServerWorldState,
328 pending_requests: &mut FxHashMap<u64, PendingRequest>, 301 pending_requests: &mut PendingRequests,
329 pool: &ThreadPool, 302 pool: &ThreadPool,
330 sender: &Sender<Task>, 303 sender: &Sender<Task>,
304 msg_sender: &Sender<RawMessage>,
331 request_received: Instant, 305 request_received: Instant,
332 req: RawRequest, 306 req: RawRequest,
333) -> Result<Option<RawRequest>> { 307) -> Result<()> {
334 let method = req.method.clone(); 308 let mut pool_dispatcher = PoolDispatcher {
335 let mut pool_dispatcher = PoolDispatcher { req: Some(req), res: None, pool, world, sender }; 309 req: Some(req),
336 let req = pool_dispatcher 310 pool,
311 world,
312 sender,
313 msg_sender,
314 pending_requests,
315 request_received,
316 };
317 pool_dispatcher
318 .on_sync::<req::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))?
319 .on_sync::<req::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
320 .on_sync::<req::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
321 .on_sync::<req::SelectionRangeRequest>(|s, p| {
322 handlers::handle_selection_range(s.snapshot(), p)
323 })?
324 .on_sync::<req::FindMatchingBrace>(|s, p| {
325 handlers::handle_find_matching_brace(s.snapshot(), p)
326 })?
337 .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? 327 .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)?
338 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? 328 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)?
339 .on::<req::ExtendSelection>(handlers::handle_extend_selection)? 329 .on::<req::ExtendSelection>(handlers::handle_extend_selection)?
340 .on::<req::SelectionRangeRequest>(handlers::handle_selection_range)?
341 .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)?
342 .on::<req::JoinLines>(handlers::handle_join_lines)?
343 .on::<req::OnEnter>(handlers::handle_on_enter)?
344 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? 330 .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)?
345 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? 331 .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)?
346 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 332 .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -363,21 +349,13 @@ fn on_request(
363 .on::<req::Formatting>(handlers::handle_formatting)? 349 .on::<req::Formatting>(handlers::handle_formatting)?
364 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? 350 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
365 .finish(); 351 .finish();
366 match req { 352 Ok(())
367 Ok(id) => {
368 let prev =
369 pending_requests.insert(id, PendingRequest { method, received: request_received });
370 assert!(prev.is_none(), "duplicate request: {}", id);
371 Ok(None)
372 }
373 Err(req) => Ok(Some(req)),
374 }
375} 353}
376 354
377fn on_notification( 355fn on_notification(
378 msg_sender: &Sender<RawMessage>, 356 msg_sender: &Sender<RawMessage>,
379 state: &mut ServerWorldState, 357 state: &mut ServerWorldState,
380 pending_requests: &mut FxHashMap<u64, PendingRequest>, 358 pending_requests: &mut PendingRequests,
381 subs: &mut Subscriptions, 359 subs: &mut Subscriptions,
382 not: RawNotification, 360 not: RawNotification,
383) -> Result<()> { 361) -> Result<()> {
@@ -389,7 +367,7 @@ fn on_notification(
389 panic!("string id's not supported: {:?}", id); 367 panic!("string id's not supported: {:?}", id);
390 } 368 }
391 }; 369 };
392 if pending_requests.remove(&id).is_some() { 370 if pending_requests.cancel(id) {
393 let response = RawResponse::err( 371 let response = RawResponse::err(
394 id, 372 id,
395 ErrorCode::RequestCanceled as i32, 373 ErrorCode::RequestCanceled as i32,
@@ -445,88 +423,140 @@ fn on_notification(
445 423
446struct PoolDispatcher<'a> { 424struct PoolDispatcher<'a> {
447 req: Option<RawRequest>, 425 req: Option<RawRequest>,
448 res: Option<u64>,
449 pool: &'a ThreadPool, 426 pool: &'a ThreadPool,
450 world: &'a mut ServerWorldState, 427 world: &'a mut ServerWorldState,
428 pending_requests: &'a mut PendingRequests,
429 msg_sender: &'a Sender<RawMessage>,
451 sender: &'a Sender<Task>, 430 sender: &'a Sender<Task>,
431 request_received: Instant,
452} 432}
453 433
454impl<'a> PoolDispatcher<'a> { 434impl<'a> PoolDispatcher<'a> {
435 /// Dispatches the request onto the current thread
436 fn on_sync<R>(
437 &mut self,
438 f: fn(&mut ServerWorldState, R::Params) -> Result<R::Result>,
439 ) -> Result<&mut Self>
440 where
441 R: req::Request + 'static,
442 R::Params: DeserializeOwned + Send + 'static,
443 R::Result: Serialize + 'static,
444 {
445 let (id, params) = match self.parse::<R>() {
446 Some(it) => it,
447 None => {
448 return Ok(self);
449 }
450 };
451 let result = f(self.world, params);
452 let task = result_to_task::<R>(id, result);
453 on_task(task, self.msg_sender, self.pending_requests, self.world);
454 Ok(self)
455 }
456
457 /// Dispatches the request onto thread pool
455 fn on<R>(&mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&mut Self> 458 fn on<R>(&mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&mut Self>
456 where 459 where
457 R: req::Request + 'static, 460 R: req::Request + 'static,
458 R::Params: DeserializeOwned + Send + 'static, 461 R::Params: DeserializeOwned + Send + 'static,
459 R::Result: Serialize + 'static, 462 R::Result: Serialize + 'static,
460 { 463 {
461 let req = match self.req.take() { 464 let (id, params) = match self.parse::<R>() {
462 None => return Ok(self), 465 Some(it) => it,
463 Some(req) => req, 466 None => {
467 return Ok(self);
468 }
464 }; 469 };
465 match req.cast::<R>() {
466 Ok((id, params)) => {
467 // Real time requests block user typing, so we should react quickly to them.
468 // Currently this means that we try to cancel background jobs if we don't have
469 // a spare thread.
470 let is_real_time = TypeId::of::<R>() == TypeId::of::<req::JoinLines>()
471 || TypeId::of::<R>() == TypeId::of::<req::OnEnter>();
472 if self.pool.queued_count() > 0 && is_real_time {
473 self.world.cancel_requests();
474 }
475 470
476 let world = self.world.snapshot(); 471 self.pool.execute({
477 let sender = self.sender.clone(); 472 let world = self.world.snapshot();
478 self.pool.execute(move || { 473 let sender = self.sender.clone();
479 let response = match f(world, params) { 474 move || {
480 Ok(resp) => RawResponse::ok::<R>(id, &resp), 475 let result = f(world, params);
481 Err(e) => match e.downcast::<LspError>() { 476 let task = result_to_task::<R>(id, result);
482 Ok(lsp_error) => { 477 sender.send(task).unwrap();
483 RawResponse::err(id, lsp_error.code, lsp_error.message)
484 }
485 Err(e) => {
486 if is_canceled(&e) {
487 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457
488 // gets fixed, we can return the proper response.
489 // This works around the issue where "content modified" error would continuously
490 // show an message pop-up in VsCode
491 // RawResponse::err(
492 // id,
493 // ErrorCode::ContentModified as i32,
494 // "content modified".to_string(),
495 // )
496 RawResponse {
497 id,
498 result: Some(serde_json::to_value(&()).unwrap()),
499 error: None,
500 }
501 } else {
502 RawResponse::err(
503 id,
504 ErrorCode::InternalError as i32,
505 format!("{}\n{}", e, e.backtrace()),
506 )
507 }
508 }
509 },
510 };
511 let task = Task::Respond(response);
512 sender.send(task).unwrap();
513 });
514 self.res = Some(id);
515 } 478 }
516 Err(req) => self.req = Some(req), 479 });
517 } 480
518 Ok(self) 481 Ok(self)
519 } 482 }
520 483
521 fn finish(&mut self) -> std::result::Result<u64, RawRequest> { 484 fn parse<R>(&mut self) -> Option<(u64, R::Params)>
522 match (self.res.take(), self.req.take()) { 485 where
523 (Some(res), None) => Ok(res), 486 R: req::Request + 'static,
524 (None, Some(req)) => Err(req), 487 R::Params: DeserializeOwned + Send + 'static,
525 _ => unreachable!(), 488 {
489 let req = self.req.take()?;
490 let (id, params) = match req.cast::<R>() {
491 Ok(it) => it,
492 Err(req) => {
493 self.req = Some(req);
494 return None;
495 }
496 };
497 self.pending_requests.start(PendingRequest {
498 id,
499 method: R::METHOD.to_string(),
500 received: self.request_received,
501 });
502 Some((id, params))
503 }
504
505 fn finish(&mut self) {
506 match self.req.take() {
507 None => (),
508 Some(req) => {
509 log::error!("unknown request: {:?}", req);
510 let resp = RawResponse::err(
511 req.id,
512 ErrorCode::MethodNotFound as i32,
513 "unknown request".to_string(),
514 );
515 self.msg_sender.send(resp.into()).unwrap();
516 }
526 } 517 }
527 } 518 }
528} 519}
529 520
521fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task
522where
523 R: req::Request + 'static,
524 R::Params: DeserializeOwned + Send + 'static,
525 R::Result: Serialize + 'static,
526{
527 let response = match result {
528 Ok(resp) => RawResponse::ok::<R>(id, &resp),
529 Err(e) => match e.downcast::<LspError>() {
530 Ok(lsp_error) => RawResponse::err(id, lsp_error.code, lsp_error.message),
531 Err(e) => {
532 if is_canceled(&e) {
533 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457
534 // gets fixed, we can return the proper response.
535 // This works around the issue where "content modified" error would continuously
536 // show an message pop-up in VsCode
537 // RawResponse::err(
538 // id,
539 // ErrorCode::ContentModified as i32,
540 // "content modified".to_string(),
541 // )
542 RawResponse {
543 id,
544 result: Some(serde_json::to_value(&()).unwrap()),
545 error: None,
546 }
547 } else {
548 RawResponse::err(
549 id,
550 ErrorCode::InternalError as i32,
551 format!("{}\n{}", e, e.backtrace()),
552 )
553 }
554 }
555 },
556 };
557 Task::Respond(response)
558}
559
530fn update_file_notifications_on_threadpool( 560fn update_file_notifications_on_threadpool(
531 pool: &ThreadPool, 561 pool: &ThreadPool,
532 world: ServerWorld, 562 world: ServerWorld,
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 0ebfd641d..8cfb6a192 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -31,10 +31,10 @@ use crate::{
31pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> { 31pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> {
32 let mut buf = world.status(); 32 let mut buf = world.status();
33 writeln!(buf, "\n\nrequests:").unwrap(); 33 writeln!(buf, "\n\nrequests:").unwrap();
34 let requests = world.latest_completed_requests.read(); 34 let requests = world.latest_requests.read();
35 for (idx, r) in requests.iter().enumerate() { 35 for (is_last, r) in requests.iter() {
36 let current = if idx == world.request_idx { "*" } else { " " }; 36 let mark = if is_last { "*" } else { " " };
37 writeln!(buf, "{:4}{}{:<36}{}ms", r.id, current, r.method, r.duration.as_millis()).unwrap(); 37 writeln!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis()).unwrap();
38 } 38 }
39 Ok(buf) 39 Ok(buf)
40} 40}
diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs
new file mode 100644
index 000000000..741770e45
--- /dev/null
+++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs
@@ -0,0 +1,72 @@
1use std::time::{Duration, Instant};
2
3use rustc_hash::FxHashMap;
4
5#[derive(Debug)]
6pub struct CompletedRequest {
7 pub id: u64,
8 pub method: String,
9 pub duration: Duration,
10}
11
12#[derive(Debug)]
13pub(crate) struct PendingRequest {
14 pub(crate) id: u64,
15 pub(crate) method: String,
16 pub(crate) received: Instant,
17}
18
19impl From<PendingRequest> for CompletedRequest {
20 fn from(pending: PendingRequest) -> CompletedRequest {
21 CompletedRequest {
22 id: pending.id,
23 method: pending.method,
24 duration: pending.received.elapsed(),
25 }
26 }
27}
28
29#[derive(Debug, Default)]
30pub(crate) struct PendingRequests {
31 map: FxHashMap<u64, PendingRequest>,
32}
33
34impl PendingRequests {
35 pub(crate) fn start(&mut self, request: PendingRequest) {
36 let id = request.id;
37 let prev = self.map.insert(id, request);
38 assert!(prev.is_none(), "duplicate request with id {}", id);
39 }
40 pub(crate) fn cancel(&mut self, id: u64) -> bool {
41 self.map.remove(&id).is_some()
42 }
43 pub(crate) fn finish(&mut self, id: u64) -> Option<CompletedRequest> {
44 self.map.remove(&id).map(CompletedRequest::from)
45 }
46}
47
48const N_COMPLETED_REQUESTS: usize = 10;
49
50#[derive(Debug, Default)]
51pub struct LatestRequests {
52 // hand-rolling VecDeque here to print things in a nicer way
53 buf: [Option<CompletedRequest>; N_COMPLETED_REQUESTS],
54 idx: usize,
55}
56
57impl LatestRequests {
58 pub(crate) fn record(&mut self, request: CompletedRequest) {
59 // special case: don't track status request itself
60 if request.method == "rust-analyzer/analyzerStatus" {
61 return;
62 }
63 let idx = self.idx;
64 self.buf[idx] = Some(request);
65 self.idx = (idx + 1) % N_COMPLETED_REQUESTS;
66 }
67
68 pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &CompletedRequest)> {
69 let idx = self.idx;
70 self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?)))
71 }
72}
diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
index 11bd952d9..470bc1205 100644
--- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs
+++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
@@ -1,21 +1,19 @@
1use ra_ide_api::FileId; 1use ra_ide_api::FileId;
2use rustc_hash::FxHashSet; 2use rustc_hash::FxHashSet;
3 3
4pub struct Subscriptions { 4#[derive(Default)]
5pub(crate) struct Subscriptions {
5 subs: FxHashSet<FileId>, 6 subs: FxHashSet<FileId>,
6} 7}
7 8
8impl Subscriptions { 9impl Subscriptions {
9 pub fn new() -> Subscriptions { 10 pub(crate) fn add_sub(&mut self, file_id: FileId) {
10 Subscriptions { subs: FxHashSet::default() }
11 }
12 pub fn add_sub(&mut self, file_id: FileId) {
13 self.subs.insert(file_id); 11 self.subs.insert(file_id);
14 } 12 }
15 pub fn remove_sub(&mut self, file_id: FileId) { 13 pub(crate) fn remove_sub(&mut self, file_id: FileId) {
16 self.subs.remove(&file_id); 14 self.subs.remove(&file_id);
17 } 15 }
18 pub fn subscriptions(&self) -> Vec<FileId> { 16 pub(crate) fn subscriptions(&self) -> Vec<FileId> {
19 self.subs.iter().cloned().collect() 17 self.subs.iter().cloned().collect()
20 } 18 }
21} 19}
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 7eb4d3e56..6076a6cd6 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -1,7 +1,6 @@
1use std::{ 1use std::{
2 path::{Path, PathBuf}, 2 path::{Path, PathBuf},
3 sync::Arc, 3 sync::Arc,
4 time::Duration,
5}; 4};
6 5
7use lsp_types::Url; 6use lsp_types::Url;
@@ -16,6 +15,7 @@ use failure::{Error, format_err};
16use gen_lsp_server::ErrorCode; 15use gen_lsp_server::ErrorCode;
17 16
18use crate::{ 17use crate::{
18 main_loop::pending_requests::{CompletedRequest, LatestRequests},
19 project_model::ProjectWorkspace, 19 project_model::ProjectWorkspace,
20 vfs_filter::IncludeRustFiles, 20 vfs_filter::IncludeRustFiles,
21 Result, 21 Result,
@@ -29,26 +29,14 @@ pub struct ServerWorldState {
29 pub workspaces: Arc<Vec<ProjectWorkspace>>, 29 pub workspaces: Arc<Vec<ProjectWorkspace>>,
30 pub analysis_host: AnalysisHost, 30 pub analysis_host: AnalysisHost,
31 pub vfs: Arc<RwLock<Vfs>>, 31 pub vfs: Arc<RwLock<Vfs>>,
32 // hand-rolling VecDeque here to print things in a nicer way 32 pub latest_requests: Arc<RwLock<LatestRequests>>,
33 pub latest_completed_requests: Arc<RwLock<[CompletedRequest; N_COMPLETED_REQUESTS]>>,
34 pub request_idx: usize,
35} 33}
36 34
37const N_COMPLETED_REQUESTS: usize = 10;
38
39pub struct ServerWorld { 35pub struct ServerWorld {
40 pub workspaces: Arc<Vec<ProjectWorkspace>>, 36 pub workspaces: Arc<Vec<ProjectWorkspace>>,
41 pub analysis: Analysis, 37 pub analysis: Analysis,
42 pub vfs: Arc<RwLock<Vfs>>, 38 pub vfs: Arc<RwLock<Vfs>>,
43 pub latest_completed_requests: Arc<RwLock<[CompletedRequest; N_COMPLETED_REQUESTS]>>, 39 pub latest_requests: Arc<RwLock<LatestRequests>>,
44 pub request_idx: usize,
45}
46
47#[derive(Debug, Default)]
48pub struct CompletedRequest {
49 pub id: u64,
50 pub method: String,
51 pub duration: Duration,
52} 40}
53 41
54impl ServerWorldState { 42impl ServerWorldState {
@@ -88,8 +76,7 @@ impl ServerWorldState {
88 workspaces: Arc::new(workspaces), 76 workspaces: Arc::new(workspaces),
89 analysis_host, 77 analysis_host,
90 vfs: Arc::new(RwLock::new(vfs)), 78 vfs: Arc::new(RwLock::new(vfs)),
91 latest_completed_requests: Default::default(), 79 latest_requests: Default::default(),
92 request_idx: 0,
93 } 80 }
94 } 81 }
95 82
@@ -149,17 +136,12 @@ impl ServerWorldState {
149 self.analysis_host.apply_change(change); 136 self.analysis_host.apply_change(change);
150 } 137 }
151 138
152 pub fn cancel_requests(&mut self) {
153 self.analysis_host.apply_change(AnalysisChange::new());
154 }
155
156 pub fn snapshot(&self) -> ServerWorld { 139 pub fn snapshot(&self) -> ServerWorld {
157 ServerWorld { 140 ServerWorld {
158 workspaces: Arc::clone(&self.workspaces), 141 workspaces: Arc::clone(&self.workspaces),
159 analysis: self.analysis_host.analysis(), 142 analysis: self.analysis_host.analysis(),
160 vfs: Arc::clone(&self.vfs), 143 vfs: Arc::clone(&self.vfs),
161 latest_completed_requests: Arc::clone(&self.latest_completed_requests), 144 latest_requests: Arc::clone(&self.latest_requests),
162 request_idx: self.request_idx.checked_sub(1).unwrap_or(N_COMPLETED_REQUESTS - 1),
163 } 145 }
164 } 146 }
165 147
@@ -172,13 +154,7 @@ impl ServerWorldState {
172 } 154 }
173 155
174 pub fn complete_request(&mut self, request: CompletedRequest) { 156 pub fn complete_request(&mut self, request: CompletedRequest) {
175 // special case: don't track status request itself 157 self.latest_requests.write().record(request)
176 if request.method == "rust-analyzer/analyzerStatus" {
177 return;
178 }
179 let idx = self.request_idx;
180 self.latest_completed_requests.write()[idx] = request;
181 self.request_idx = (idx + 1) % N_COMPLETED_REQUESTS;
182 } 158 }
183} 159}
184 160