aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/global_state.rs8
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs125
-rw-r--r--crates/rust-analyzer/src/main_loop/req_queue.rs123
-rw-r--r--crates/rust-analyzer/src/main_loop/request_metrics.rs37
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
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.2" 35lsp-server = "0.3.3"
36ra_flycheck = { path = "../ra_flycheck" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_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;
20use crate::{ 20use 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
33use serde::de::DeserializeOwned; 33use serde::de::DeserializeOwned;
34 34
35pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 35pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
36pub use crate::{ 36pub 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
4mod handlers; 4mod handlers;
5mod subscriptions; 5mod subscriptions;
6pub(crate) mod req_queue; 6pub(crate) mod request_metrics;
7 7
8use std::{ 8use std::{
9 borrow::Cow, 9 borrow::Cow,
@@ -17,11 +17,13 @@ use std::{
17}; 17};
18 18
19use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{
21 Connection, ErrorCode, Message, Notification, ReqQueue, Request, RequestId, Response,
22};
21use lsp_types::{ 23use 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};
26use ra_flycheck::{CheckTask, Status}; 28use ra_flycheck::{CheckTask, Status};
27use ra_ide::{Canceled, FileId, LineIndex}; 29use 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};
43use req_queue::ReqQueue;
44 45
45#[derive(Debug)] 46#[derive(Debug)]
46pub struct LspError { 47pub 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
266type ReqHandler = fn(&mut GlobalState, Response);
267const DO_NOTHING: ReqHandler = |_, _| ();
268type Incoming = lsp_server::Incoming<(&'static str, Instant)>;
269
264#[derive(Default)] 270#[derive(Default)]
265struct LoopState { 271struct 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(
367fn on_task( 373fn 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
388fn on_request( 399fn 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.
2use std::time::{Duration, Instant};
3
4use lsp_server::RequestId;
5use rustc_hash::FxHashMap;
6use serde::Serialize;
7
8#[derive(Debug)]
9pub(crate) struct ReqQueue<H> {
10 pub(crate) incoming: Incoming,
11 pub(crate) outgoing: Outgoing<H>,
12}
13
14impl<H> Default for ReqQueue<H> {
15 fn default() -> Self {
16 ReqQueue { incoming: Incoming::default(), outgoing: Outgoing::default() }
17 }
18}
19
20#[derive(Debug)]
21pub(crate) struct Outgoing<H> {
22 next: u64,
23 pending: FxHashMap<RequestId, H>,
24}
25
26impl<H> Default for Outgoing<H> {
27 fn default() -> Self {
28 Outgoing { next: 0, pending: FxHashMap::default() }
29 }
30}
31
32impl<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)]
49pub(crate) struct CompletedInRequest {
50 pub(crate) id: RequestId,
51 pub(crate) method: String,
52 pub(crate) duration: Duration,
53}
54
55#[derive(Debug)]
56pub(crate) struct PendingInRequest {
57 pub(crate) id: RequestId,
58 pub(crate) method: String,
59 pub(crate) received: Instant,
60}
61
62impl 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)]
73pub(crate) struct Incoming {
74 pending: FxHashMap<RequestId, PendingInRequest>,
75}
76
77impl 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
99const N_COMPLETED_REQUESTS: usize = 10;
100
101#[derive(Debug, Default)]
102pub 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
108impl 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
2use std::time::Duration;
3
4use lsp_server::RequestId;
5
6#[derive(Debug)]
7pub(crate) struct RequestMetrics {
8 pub(crate) id: RequestId,
9 pub(crate) method: String,
10 pub(crate) duration: Duration,
11}
12
13const N_COMPLETED_REQUESTS: usize = 10;
14
15#[derive(Debug, Default)]
16pub(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
22impl 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}