diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 125 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/req_queue.rs | 123 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/request_metrics.rs | 37 |
6 files changed, 107 insertions, 190 deletions
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 458089e53..2b46e8905 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -32,7 +32,7 @@ threadpool = "1.7.1" | |||
32 | 32 | ||
33 | stdx = { path = "../stdx" } | 33 | stdx = { path = "../stdx" } |
34 | 34 | ||
35 | lsp-server = "0.3.2" | 35 | lsp-server = "0.3.3" |
36 | ra_flycheck = { path = "../ra_flycheck" } | 36 | ra_flycheck = { path = "../ra_flycheck" } |
37 | ra_ide = { path = "../ra_ide" } | 37 | ra_ide = { path = "../ra_ide" } |
38 | ra_prof = { path = "../ra_prof" } | 38 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index d1897bf50..d04ef4c61 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -20,7 +20,7 @@ use stdx::format_to; | |||
20 | use crate::{ | 20 | use crate::{ |
21 | config::{Config, FilesWatcher}, | 21 | config::{Config, FilesWatcher}, |
22 | diagnostics::{CheckFixes, DiagnosticCollection}, | 22 | diagnostics::{CheckFixes, DiagnosticCollection}, |
23 | main_loop::req_queue::{CompletedInRequest, LatestRequests}, | 23 | main_loop::request_metrics::{LatestRequests, RequestMetrics}, |
24 | to_proto::url_from_abs_path, | 24 | to_proto::url_from_abs_path, |
25 | vfs_glob::{Glob, RustPackageFilterBuilder}, | 25 | vfs_glob::{Glob, RustPackageFilterBuilder}, |
26 | LspError, Result, | 26 | LspError, Result, |
@@ -55,10 +55,10 @@ pub struct GlobalState { | |||
55 | pub analysis_host: AnalysisHost, | 55 | pub analysis_host: AnalysisHost, |
56 | pub vfs: Arc<RwLock<Vfs>>, | 56 | pub vfs: Arc<RwLock<Vfs>>, |
57 | pub task_receiver: Receiver<VfsTask>, | 57 | pub task_receiver: Receiver<VfsTask>, |
58 | pub latest_requests: Arc<RwLock<LatestRequests>>, | ||
59 | pub flycheck: Option<Flycheck>, | 58 | pub flycheck: Option<Flycheck>, |
60 | pub diagnostics: DiagnosticCollection, | 59 | pub diagnostics: DiagnosticCollection, |
61 | pub proc_macro_client: ProcMacroClient, | 60 | pub proc_macro_client: ProcMacroClient, |
61 | pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, | ||
62 | } | 62 | } |
63 | 63 | ||
64 | /// An immutable snapshot of the world's state at a point in time. | 64 | /// An immutable snapshot of the world's state at a point in time. |
@@ -66,8 +66,8 @@ pub struct GlobalStateSnapshot { | |||
66 | pub config: Config, | 66 | pub config: Config, |
67 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 67 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
68 | pub analysis: Analysis, | 68 | pub analysis: Analysis, |
69 | pub latest_requests: Arc<RwLock<LatestRequests>>, | ||
70 | pub check_fixes: CheckFixes, | 69 | pub check_fixes: CheckFixes, |
70 | pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, | ||
71 | vfs: Arc<RwLock<Vfs>>, | 71 | vfs: Arc<RwLock<Vfs>>, |
72 | } | 72 | } |
73 | 73 | ||
@@ -236,7 +236,7 @@ impl GlobalState { | |||
236 | self.analysis_host.collect_garbage() | 236 | self.analysis_host.collect_garbage() |
237 | } | 237 | } |
238 | 238 | ||
239 | pub(crate) fn complete_request(&mut self, request: CompletedInRequest) { | 239 | pub(crate) fn complete_request(&mut self, request: RequestMetrics) { |
240 | self.latest_requests.write().record(request) | 240 | self.latest_requests.write().record(request) |
241 | } | 241 | } |
242 | } | 242 | } |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 609cb69d3..64e70955f 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -32,7 +32,7 @@ mod semantic_tokens; | |||
32 | 32 | ||
33 | use serde::de::DeserializeOwned; | 33 | use serde::de::DeserializeOwned; |
34 | 34 | ||
35 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; | 35 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; |
36 | pub use crate::{ | 36 | pub use crate::{ |
37 | caps::server_capabilities, | 37 | caps::server_capabilities, |
38 | main_loop::LspError, | 38 | main_loop::LspError, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index fd40b2443..674b1323b 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | mod handlers; | 4 | mod handlers; |
5 | mod subscriptions; | 5 | mod subscriptions; |
6 | pub(crate) mod req_queue; | 6 | pub(crate) mod request_metrics; |
7 | 7 | ||
8 | use std::{ | 8 | use std::{ |
9 | borrow::Cow, | 9 | borrow::Cow, |
@@ -17,11 +17,13 @@ use std::{ | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
20 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 20 | use lsp_server::{ |
21 | Connection, ErrorCode, Message, Notification, ReqQueue, Request, RequestId, Response, | ||
22 | }; | ||
21 | use lsp_types::{ | 23 | use lsp_types::{ |
22 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, | 24 | request::Request as _, DidChangeTextDocumentParams, NumberOrString, |
23 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, | 25 | TextDocumentContentChangeEvent, WorkDoneProgress, WorkDoneProgressBegin, |
24 | WorkDoneProgressReport, | 26 | WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport, |
25 | }; | 27 | }; |
26 | use ra_flycheck::{CheckTask, Status}; | 28 | use ra_flycheck::{CheckTask, Status}; |
27 | use ra_ide::{Canceled, FileId, LineIndex}; | 29 | use ra_ide::{Canceled, FileId, LineIndex}; |
@@ -37,10 +39,9 @@ use crate::{ | |||
37 | from_proto, | 39 | from_proto, |
38 | global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot}, | 40 | global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot}, |
39 | lsp_ext, | 41 | lsp_ext, |
40 | main_loop::subscriptions::Subscriptions, | 42 | main_loop::{request_metrics::RequestMetrics, subscriptions::Subscriptions}, |
41 | Result, | 43 | Result, |
42 | }; | 44 | }; |
43 | use req_queue::ReqQueue; | ||
44 | 45 | ||
45 | #[derive(Debug)] | 46 | #[derive(Debug)] |
46 | pub struct LspError { | 47 | pub struct LspError { |
@@ -150,10 +151,11 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
150 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 151 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
151 | }; | 152 | }; |
152 | let params = lsp_types::RegistrationParams { registrations: vec![registration] }; | 153 | let params = lsp_types::RegistrationParams { registrations: vec![registration] }; |
153 | let request = loop_state | 154 | let request = loop_state.req_queue.outgoing.register( |
154 | .req_queue | 155 | lsp_types::request::RegisterCapability::METHOD.to_string(), |
155 | .outgoing | 156 | params, |
156 | .register::<lsp_types::request::RegisterCapability>(params, |_, _| ()); | 157 | DO_NOTHING, |
158 | ); | ||
157 | connection.sender.send(request.into()).unwrap(); | 159 | connection.sender.send(request.into()).unwrap(); |
158 | } | 160 | } |
159 | 161 | ||
@@ -261,9 +263,13 @@ impl fmt::Debug for Event { | |||
261 | } | 263 | } |
262 | } | 264 | } |
263 | 265 | ||
266 | type ReqHandler = fn(&mut GlobalState, Response); | ||
267 | const DO_NOTHING: ReqHandler = |_, _| (); | ||
268 | type Incoming = lsp_server::Incoming<(&'static str, Instant)>; | ||
269 | |||
264 | #[derive(Default)] | 270 | #[derive(Default)] |
265 | struct LoopState { | 271 | struct LoopState { |
266 | req_queue: ReqQueue<fn(&mut GlobalState, lsp_server::Response)>, | 272 | req_queue: ReqQueue<(&'static str, Instant), ReqHandler>, |
267 | subscriptions: Subscriptions, | 273 | subscriptions: Subscriptions, |
268 | workspace_loaded: bool, | 274 | workspace_loaded: bool, |
269 | roots_progress_reported: Option<usize>, | 275 | roots_progress_reported: Option<usize>, |
@@ -367,14 +373,19 @@ fn loop_turn( | |||
367 | fn on_task( | 373 | fn on_task( |
368 | task: Task, | 374 | task: Task, |
369 | msg_sender: &Sender<Message>, | 375 | msg_sender: &Sender<Message>, |
370 | incoming_requests: &mut req_queue::Incoming, | 376 | incoming_requests: &mut Incoming, |
371 | state: &mut GlobalState, | 377 | state: &mut GlobalState, |
372 | ) { | 378 | ) { |
373 | match task { | 379 | match task { |
374 | Task::Respond(response) => { | 380 | Task::Respond(response) => { |
375 | if let Some(completed) = incoming_requests.complete(response.id.clone()) { | 381 | if let Some((method, start)) = incoming_requests.complete(response.id.clone()) { |
376 | log::info!("handled req#{} in {:?}", completed.id, completed.duration); | 382 | let duration = start.elapsed(); |
377 | state.complete_request(completed); | 383 | log::info!("handled req#{} in {:?}", response.id, duration); |
384 | state.complete_request(RequestMetrics { | ||
385 | id: response.id.clone(), | ||
386 | method: method.to_string(), | ||
387 | duration, | ||
388 | }); | ||
378 | msg_sender.send(response.into()).unwrap(); | 389 | msg_sender.send(response.into()).unwrap(); |
379 | } | 390 | } |
380 | } | 391 | } |
@@ -387,7 +398,7 @@ fn on_task( | |||
387 | 398 | ||
388 | fn on_request( | 399 | fn on_request( |
389 | global_state: &mut GlobalState, | 400 | global_state: &mut GlobalState, |
390 | incoming_requests: &mut req_queue::Incoming, | 401 | incoming_requests: &mut Incoming, |
391 | pool: &ThreadPool, | 402 | pool: &ThreadPool, |
392 | task_sender: &Sender<Task>, | 403 | task_sender: &Sender<Task>, |
393 | msg_sender: &Sender<Message>, | 404 | msg_sender: &Sender<Message>, |
@@ -527,37 +538,35 @@ fn on_notification( | |||
527 | Ok(_) => { | 538 | Ok(_) => { |
528 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, | 539 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, |
529 | // this notification's parameters should be ignored and the actual config queried separately. | 540 | // this notification's parameters should be ignored and the actual config queried separately. |
530 | let request = loop_state | 541 | let request = loop_state.req_queue.outgoing.register( |
531 | .req_queue | 542 | lsp_types::request::WorkspaceConfiguration::METHOD.to_string(), |
532 | .outgoing | 543 | lsp_types::ConfigurationParams { |
533 | .register::<lsp_types::request::WorkspaceConfiguration>( | 544 | items: vec![lsp_types::ConfigurationItem { |
534 | lsp_types::ConfigurationParams { | 545 | scope_uri: None, |
535 | items: vec![lsp_types::ConfigurationItem { | 546 | section: Some("rust-analyzer".to_string()), |
536 | scope_uri: None, | 547 | }], |
537 | section: Some("rust-analyzer".to_string()), | 548 | }, |
538 | }], | 549 | |global_state, resp| { |
539 | }, | 550 | log::debug!("config update response: '{:?}", resp); |
540 | |global_state, resp| { | 551 | let Response { error, result, .. } = resp; |
541 | log::debug!("config update response: '{:?}", resp); | 552 | |
542 | let Response { error, result, .. } = resp; | 553 | match (error, result) { |
543 | 554 | (Some(err), _) => { | |
544 | match (error, result) { | 555 | log::error!("failed to fetch the server settings: {:?}", err) |
545 | (Some(err), _) => { | 556 | } |
546 | log::error!("failed to fetch the server settings: {:?}", err) | 557 | (None, Some(configs)) => { |
547 | } | 558 | if let Some(new_config) = configs.get(0) { |
548 | (None, Some(configs)) => { | 559 | let mut config = global_state.config.clone(); |
549 | if let Some(new_config) = configs.get(0) { | 560 | config.update(&new_config); |
550 | let mut config = global_state.config.clone(); | 561 | global_state.update_configuration(config); |
551 | config.update(&new_config); | ||
552 | global_state.update_configuration(config); | ||
553 | } | ||
554 | } | 562 | } |
555 | (None, None) => log::error!( | ||
556 | "received empty server settings response from the client" | ||
557 | ), | ||
558 | } | 563 | } |
559 | }, | 564 | (None, None) => { |
560 | ); | 565 | log::error!("received empty server settings response from the client") |
566 | } | ||
567 | } | ||
568 | }, | ||
569 | ); | ||
561 | msg_sender.send(request.into())?; | 570 | msg_sender.send(request.into())?; |
562 | 571 | ||
563 | return Ok(()); | 572 | return Ok(()); |
@@ -727,15 +736,13 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { | |||
727 | 736 | ||
728 | match (prev, loop_state.workspace_loaded) { | 737 | match (prev, loop_state.workspace_loaded) { |
729 | (None, false) => { | 738 | (None, false) => { |
730 | let request = loop_state | 739 | let request = loop_state.req_queue.outgoing.register( |
731 | .req_queue | 740 | lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), |
732 | .outgoing | 741 | WorkDoneProgressCreateParams { |
733 | .register::<lsp_types::request::WorkDoneProgressCreate>( | 742 | token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), |
734 | WorkDoneProgressCreateParams { | 743 | }, |
735 | token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), | 744 | DO_NOTHING, |
736 | }, | 745 | ); |
737 | |_, _| (), | ||
738 | ); | ||
739 | sender.send(request.into()).unwrap(); | 746 | sender.send(request.into()).unwrap(); |
740 | send_startup_progress_notif( | 747 | send_startup_progress_notif( |
741 | sender, | 748 | sender, |
@@ -778,7 +785,7 @@ struct PoolDispatcher<'a> { | |||
778 | req: Option<Request>, | 785 | req: Option<Request>, |
779 | pool: &'a ThreadPool, | 786 | pool: &'a ThreadPool, |
780 | global_state: &'a mut GlobalState, | 787 | global_state: &'a mut GlobalState, |
781 | incoming_requests: &'a mut req_queue::Incoming, | 788 | incoming_requests: &'a mut Incoming, |
782 | msg_sender: &'a Sender<Message>, | 789 | msg_sender: &'a Sender<Message>, |
783 | task_sender: &'a Sender<Task>, | 790 | task_sender: &'a Sender<Task>, |
784 | request_received: Instant, | 791 | request_received: Instant, |
@@ -854,11 +861,7 @@ impl<'a> PoolDispatcher<'a> { | |||
854 | return None; | 861 | return None; |
855 | } | 862 | } |
856 | }; | 863 | }; |
857 | self.incoming_requests.register(req_queue::PendingInRequest { | 864 | self.incoming_requests.register(id.clone(), (R::METHOD, self.request_received)); |
858 | id: id.clone(), | ||
859 | method: R::METHOD.to_string(), | ||
860 | received: self.request_received, | ||
861 | }); | ||
862 | Some((id, params)) | 865 | Some((id, params)) |
863 | } | 866 | } |
864 | 867 | ||
diff --git a/crates/rust-analyzer/src/main_loop/req_queue.rs b/crates/rust-analyzer/src/main_loop/req_queue.rs deleted file mode 100644 index 5cf6d916b..000000000 --- a/crates/rust-analyzer/src/main_loop/req_queue.rs +++ /dev/null | |||
@@ -1,123 +0,0 @@ | |||
1 | //! Manages the set of in-flight requests in both directions. | ||
2 | use std::time::{Duration, Instant}; | ||
3 | |||
4 | use lsp_server::RequestId; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use serde::Serialize; | ||
7 | |||
8 | #[derive(Debug)] | ||
9 | pub(crate) struct ReqQueue<H> { | ||
10 | pub(crate) incoming: Incoming, | ||
11 | pub(crate) outgoing: Outgoing<H>, | ||
12 | } | ||
13 | |||
14 | impl<H> Default for ReqQueue<H> { | ||
15 | fn default() -> Self { | ||
16 | ReqQueue { incoming: Incoming::default(), outgoing: Outgoing::default() } | ||
17 | } | ||
18 | } | ||
19 | |||
20 | #[derive(Debug)] | ||
21 | pub(crate) struct Outgoing<H> { | ||
22 | next: u64, | ||
23 | pending: FxHashMap<RequestId, H>, | ||
24 | } | ||
25 | |||
26 | impl<H> Default for Outgoing<H> { | ||
27 | fn default() -> Self { | ||
28 | Outgoing { next: 0, pending: FxHashMap::default() } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | impl<H> Outgoing<H> { | ||
33 | pub(crate) fn register<R>(&mut self, params: R::Params, handler: H) -> lsp_server::Request | ||
34 | where | ||
35 | R: lsp_types::request::Request, | ||
36 | R::Params: Serialize, | ||
37 | { | ||
38 | let id = RequestId::from(self.next); | ||
39 | self.next += 1; | ||
40 | self.pending.insert(id.clone(), handler); | ||
41 | lsp_server::Request::new(id, R::METHOD.to_string(), params) | ||
42 | } | ||
43 | pub(crate) fn complete(&mut self, id: RequestId) -> H { | ||
44 | self.pending.remove(&id).unwrap() | ||
45 | } | ||
46 | } | ||
47 | |||
48 | #[derive(Debug)] | ||
49 | pub(crate) struct CompletedInRequest { | ||
50 | pub(crate) id: RequestId, | ||
51 | pub(crate) method: String, | ||
52 | pub(crate) duration: Duration, | ||
53 | } | ||
54 | |||
55 | #[derive(Debug)] | ||
56 | pub(crate) struct PendingInRequest { | ||
57 | pub(crate) id: RequestId, | ||
58 | pub(crate) method: String, | ||
59 | pub(crate) received: Instant, | ||
60 | } | ||
61 | |||
62 | impl From<PendingInRequest> for CompletedInRequest { | ||
63 | fn from(pending: PendingInRequest) -> CompletedInRequest { | ||
64 | CompletedInRequest { | ||
65 | id: pending.id, | ||
66 | method: pending.method, | ||
67 | duration: pending.received.elapsed(), | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[derive(Debug, Default)] | ||
73 | pub(crate) struct Incoming { | ||
74 | pending: FxHashMap<RequestId, PendingInRequest>, | ||
75 | } | ||
76 | |||
77 | impl Incoming { | ||
78 | pub(crate) fn register(&mut self, request: PendingInRequest) { | ||
79 | let id = request.id.clone(); | ||
80 | let prev = self.pending.insert(id.clone(), request); | ||
81 | assert!(prev.is_none(), "duplicate request with id {}", id); | ||
82 | } | ||
83 | pub(crate) fn cancel(&mut self, id: RequestId) -> Option<lsp_server::Response> { | ||
84 | if self.pending.remove(&id).is_some() { | ||
85 | Some(lsp_server::Response::new_err( | ||
86 | id, | ||
87 | lsp_server::ErrorCode::RequestCanceled as i32, | ||
88 | "canceled by client".to_string(), | ||
89 | )) | ||
90 | } else { | ||
91 | None | ||
92 | } | ||
93 | } | ||
94 | pub(crate) fn complete(&mut self, id: RequestId) -> Option<CompletedInRequest> { | ||
95 | self.pending.remove(&id).map(CompletedInRequest::from) | ||
96 | } | ||
97 | } | ||
98 | |||
99 | const N_COMPLETED_REQUESTS: usize = 10; | ||
100 | |||
101 | #[derive(Debug, Default)] | ||
102 | pub struct LatestRequests { | ||
103 | // hand-rolling VecDeque here to print things in a nicer way | ||
104 | buf: [Option<CompletedInRequest>; N_COMPLETED_REQUESTS], | ||
105 | idx: usize, | ||
106 | } | ||
107 | |||
108 | impl LatestRequests { | ||
109 | pub(crate) fn record(&mut self, request: CompletedInRequest) { | ||
110 | // special case: don't track status request itself | ||
111 | if request.method == "rust-analyzer/analyzerStatus" { | ||
112 | return; | ||
113 | } | ||
114 | let idx = self.idx; | ||
115 | self.buf[idx] = Some(request); | ||
116 | self.idx = (idx + 1) % N_COMPLETED_REQUESTS; | ||
117 | } | ||
118 | |||
119 | pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &CompletedInRequest)> { | ||
120 | let idx = self.idx; | ||
121 | self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?))) | ||
122 | } | ||
123 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop/request_metrics.rs b/crates/rust-analyzer/src/main_loop/request_metrics.rs new file mode 100644 index 000000000..b1019e2d6 --- /dev/null +++ b/crates/rust-analyzer/src/main_loop/request_metrics.rs | |||
@@ -0,0 +1,37 @@ | |||
1 | //! Records stats about requests | ||
2 | use std::time::Duration; | ||
3 | |||
4 | use lsp_server::RequestId; | ||
5 | |||
6 | #[derive(Debug)] | ||
7 | pub(crate) struct RequestMetrics { | ||
8 | pub(crate) id: RequestId, | ||
9 | pub(crate) method: String, | ||
10 | pub(crate) duration: Duration, | ||
11 | } | ||
12 | |||
13 | const N_COMPLETED_REQUESTS: usize = 10; | ||
14 | |||
15 | #[derive(Debug, Default)] | ||
16 | pub(crate) struct LatestRequests { | ||
17 | // hand-rolling VecDeque here to print things in a nicer way | ||
18 | buf: [Option<RequestMetrics>; N_COMPLETED_REQUESTS], | ||
19 | idx: usize, | ||
20 | } | ||
21 | |||
22 | impl LatestRequests { | ||
23 | pub(crate) fn record(&mut self, request: RequestMetrics) { | ||
24 | // special case: don't track status request itself | ||
25 | if request.method == "rust-analyzer/analyzerStatus" { | ||
26 | return; | ||
27 | } | ||
28 | let idx = self.idx; | ||
29 | self.buf[idx] = Some(request); | ||
30 | self.idx = (idx + 1) % N_COMPLETED_REQUESTS; | ||
31 | } | ||
32 | |||
33 | pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &RequestMetrics)> { | ||
34 | let idx = self.idx; | ||
35 | self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?))) | ||
36 | } | ||
37 | } | ||