diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-06-25 21:46:08 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-06-25 21:46:08 +0100 |
commit | 3615347fcebebaa58a5cbf675ae062aae149d9c3 (patch) | |
tree | 77c2232aa7286e2880a28678e343286b853bfebc /crates | |
parent | b5aa92bab94880a289372c66c99117ae5e6e9e1f (diff) | |
parent | df769e5bb4e26a2f150ce2c37b9b9364ee10bab8 (diff) |
Merge #5065
5065: Simplify diagnostics handling r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/rust-analyzer/src/diagnostics.rs | 54 | ||||
-rw-r--r-- | crates/rust-analyzer/src/dispatch.rs | 44 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 5 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 389 |
5 files changed, 239 insertions, 267 deletions
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 290609e7f..f3cdb842b 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -1,14 +1,15 @@ | |||
1 | //! Book keeping for keeping diagnostics easily in sync with the client. | 1 | //! Book keeping for keeping diagnostics easily in sync with the client. |
2 | pub(crate) mod to_proto; | 2 | pub(crate) mod to_proto; |
3 | 3 | ||
4 | use std::{collections::HashMap, sync::Arc}; | 4 | use std::{collections::HashMap, mem, sync::Arc}; |
5 | 5 | ||
6 | use lsp_types::{Diagnostic, Range}; | 6 | use lsp_types::{Diagnostic, Range}; |
7 | use ra_ide::FileId; | 7 | use ra_ide::FileId; |
8 | use rustc_hash::FxHashSet; | ||
8 | 9 | ||
9 | use crate::lsp_ext; | 10 | use crate::lsp_ext; |
10 | 11 | ||
11 | pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; | 12 | pub(crate) type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; |
12 | 13 | ||
13 | #[derive(Debug, Default, Clone)] | 14 | #[derive(Debug, Default, Clone)] |
14 | pub struct DiagnosticsConfig { | 15 | pub struct DiagnosticsConfig { |
@@ -17,32 +18,26 @@ pub struct DiagnosticsConfig { | |||
17 | } | 18 | } |
18 | 19 | ||
19 | #[derive(Debug, Default, Clone)] | 20 | #[derive(Debug, Default, Clone)] |
20 | pub struct DiagnosticCollection { | 21 | pub(crate) struct DiagnosticCollection { |
21 | pub native: HashMap<FileId, Vec<Diagnostic>>, | 22 | pub(crate) native: HashMap<FileId, Vec<Diagnostic>>, |
22 | pub check: HashMap<FileId, Vec<Diagnostic>>, | 23 | pub(crate) check: HashMap<FileId, Vec<Diagnostic>>, |
23 | pub check_fixes: CheckFixes, | 24 | pub(crate) check_fixes: CheckFixes, |
25 | changes: FxHashSet<FileId>, | ||
24 | } | 26 | } |
25 | 27 | ||
26 | #[derive(Debug, Clone)] | 28 | #[derive(Debug, Clone)] |
27 | pub struct Fix { | 29 | pub(crate) struct Fix { |
28 | pub range: Range, | 30 | pub(crate) range: Range, |
29 | pub action: lsp_ext::CodeAction, | 31 | pub(crate) action: lsp_ext::CodeAction, |
30 | } | ||
31 | |||
32 | #[derive(Debug)] | ||
33 | pub enum DiagnosticTask { | ||
34 | ClearCheck, | ||
35 | AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>), | ||
36 | SetNative(FileId, Vec<Diagnostic>), | ||
37 | } | 32 | } |
38 | 33 | ||
39 | impl DiagnosticCollection { | 34 | impl DiagnosticCollection { |
40 | pub fn clear_check(&mut self) -> Vec<FileId> { | 35 | pub(crate) fn clear_check(&mut self) { |
41 | Arc::make_mut(&mut self.check_fixes).clear(); | 36 | Arc::make_mut(&mut self.check_fixes).clear(); |
42 | self.check.drain().map(|(key, _value)| key).collect() | 37 | self.changes.extend(self.check.drain().map(|(key, _value)| key)) |
43 | } | 38 | } |
44 | 39 | ||
45 | pub fn add_check_diagnostic( | 40 | pub(crate) fn add_check_diagnostic( |
46 | &mut self, | 41 | &mut self, |
47 | file_id: FileId, | 42 | file_id: FileId, |
48 | diagnostic: Diagnostic, | 43 | diagnostic: Diagnostic, |
@@ -61,30 +56,25 @@ impl DiagnosticCollection { | |||
61 | .or_default() | 56 | .or_default() |
62 | .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action })); | 57 | .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action })); |
63 | diagnostics.push(diagnostic); | 58 | diagnostics.push(diagnostic); |
59 | self.changes.insert(file_id); | ||
64 | } | 60 | } |
65 | 61 | ||
66 | pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) { | 62 | pub(crate) fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) { |
67 | self.native.insert(file_id, diagnostics); | 63 | self.native.insert(file_id, diagnostics); |
64 | self.changes.insert(file_id); | ||
68 | } | 65 | } |
69 | 66 | ||
70 | pub fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> { | 67 | pub(crate) fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> { |
71 | let native = self.native.get(&file_id).into_iter().flatten(); | 68 | let native = self.native.get(&file_id).into_iter().flatten(); |
72 | let check = self.check.get(&file_id).into_iter().flatten(); | 69 | let check = self.check.get(&file_id).into_iter().flatten(); |
73 | native.chain(check) | 70 | native.chain(check) |
74 | } | 71 | } |
75 | 72 | ||
76 | pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec<FileId> { | 73 | pub(crate) fn take_changes(&mut self) -> Option<FxHashSet<FileId>> { |
77 | match task { | 74 | if self.changes.is_empty() { |
78 | DiagnosticTask::ClearCheck => self.clear_check(), | 75 | return None; |
79 | DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => { | ||
80 | self.add_check_diagnostic(file_id, diagnostic, fixes); | ||
81 | vec![file_id] | ||
82 | } | ||
83 | DiagnosticTask::SetNative(file_id, diagnostics) => { | ||
84 | self.set_native_diagnostics(file_id, diagnostics); | ||
85 | vec![file_id] | ||
86 | } | ||
87 | } | 76 | } |
77 | Some(mem::take(&mut self.changes)) | ||
88 | } | 78 | } |
89 | } | 79 | } |
90 | 80 | ||
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 5fdbed8ef..03b373dee 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! A visitor for downcasting arbitrary request (JSON) into a specific type. | 1 | //! A visitor for downcasting arbitrary request (JSON) into a specific type. |
2 | use std::{panic, time::Instant}; | 2 | use std::panic; |
3 | 3 | ||
4 | use serde::{de::DeserializeOwned, Serialize}; | 4 | use serde::{de::DeserializeOwned, Serialize}; |
5 | 5 | ||
@@ -13,7 +13,6 @@ use crate::{ | |||
13 | pub(crate) struct RequestDispatcher<'a> { | 13 | pub(crate) struct RequestDispatcher<'a> { |
14 | pub(crate) req: Option<lsp_server::Request>, | 14 | pub(crate) req: Option<lsp_server::Request>, |
15 | pub(crate) global_state: &'a mut GlobalState, | 15 | pub(crate) global_state: &'a mut GlobalState, |
16 | pub(crate) request_received: Instant, | ||
17 | } | 16 | } |
18 | 17 | ||
19 | impl<'a> RequestDispatcher<'a> { | 18 | impl<'a> RequestDispatcher<'a> { |
@@ -34,12 +33,12 @@ impl<'a> RequestDispatcher<'a> { | |||
34 | } | 33 | } |
35 | }; | 34 | }; |
36 | let world = panic::AssertUnwindSafe(&mut *self.global_state); | 35 | let world = panic::AssertUnwindSafe(&mut *self.global_state); |
37 | let task = panic::catch_unwind(move || { | 36 | let response = panic::catch_unwind(move || { |
38 | let result = f(world.0, params); | 37 | let result = f(world.0, params); |
39 | result_to_task::<R>(id, result) | 38 | result_to_response::<R>(id, result) |
40 | }) | 39 | }) |
41 | .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; | 40 | .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; |
42 | self.global_state.on_task(task); | 41 | self.global_state.respond(response); |
43 | Ok(self) | 42 | Ok(self) |
44 | } | 43 | } |
45 | 44 | ||
@@ -64,7 +63,7 @@ impl<'a> RequestDispatcher<'a> { | |||
64 | let world = self.global_state.snapshot(); | 63 | let world = self.global_state.snapshot(); |
65 | move || { | 64 | move || { |
66 | let result = f(world, params); | 65 | let result = f(world, params); |
67 | result_to_task::<R>(id, result) | 66 | Task::Response(result_to_response::<R>(id, result)) |
68 | } | 67 | } |
69 | }); | 68 | }); |
70 | 69 | ||
@@ -72,17 +71,14 @@ impl<'a> RequestDispatcher<'a> { | |||
72 | } | 71 | } |
73 | 72 | ||
74 | pub(crate) fn finish(&mut self) { | 73 | pub(crate) fn finish(&mut self) { |
75 | match self.req.take() { | 74 | if let Some(req) = self.req.take() { |
76 | None => (), | 75 | log::error!("unknown request: {:?}", req); |
77 | Some(req) => { | 76 | let response = lsp_server::Response::new_err( |
78 | log::error!("unknown request: {:?}", req); | 77 | req.id, |
79 | let resp = lsp_server::Response::new_err( | 78 | lsp_server::ErrorCode::MethodNotFound as i32, |
80 | req.id, | 79 | "unknown request".to_string(), |
81 | lsp_server::ErrorCode::MethodNotFound as i32, | 80 | ); |
82 | "unknown request".to_string(), | 81 | self.global_state.respond(response) |
83 | ); | ||
84 | self.global_state.send(resp.into()); | ||
85 | } | ||
86 | } | 82 | } |
87 | } | 83 | } |
88 | 84 | ||
@@ -99,21 +95,20 @@ impl<'a> RequestDispatcher<'a> { | |||
99 | return None; | 95 | return None; |
100 | } | 96 | } |
101 | }; | 97 | }; |
102 | self.global_state | ||
103 | .req_queue | ||
104 | .incoming | ||
105 | .register(id.clone(), (R::METHOD, self.request_received)); | ||
106 | Some((id, params)) | 98 | Some((id, params)) |
107 | } | 99 | } |
108 | } | 100 | } |
109 | 101 | ||
110 | fn result_to_task<R>(id: lsp_server::RequestId, result: Result<R::Result>) -> Task | 102 | fn result_to_response<R>( |
103 | id: lsp_server::RequestId, | ||
104 | result: Result<R::Result>, | ||
105 | ) -> lsp_server::Response | ||
111 | where | 106 | where |
112 | R: lsp_types::request::Request + 'static, | 107 | R: lsp_types::request::Request + 'static, |
113 | R::Params: DeserializeOwned + 'static, | 108 | R::Params: DeserializeOwned + 'static, |
114 | R::Result: Serialize + 'static, | 109 | R::Result: Serialize + 'static, |
115 | { | 110 | { |
116 | let response = match result { | 111 | match result { |
117 | Ok(resp) => lsp_server::Response::new_ok(id, &resp), | 112 | Ok(resp) => lsp_server::Response::new_ok(id, &resp), |
118 | Err(e) => match e.downcast::<LspError>() { | 113 | Err(e) => match e.downcast::<LspError>() { |
119 | Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), | 114 | Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), |
@@ -133,8 +128,7 @@ where | |||
133 | } | 128 | } |
134 | } | 129 | } |
135 | }, | 130 | }, |
136 | }; | 131 | } |
137 | Task::Respond(response) | ||
138 | } | 132 | } |
139 | 133 | ||
140 | pub(crate) struct NotificationDispatcher<'a> { | 134 | pub(crate) struct NotificationDispatcher<'a> { |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 56d50c789..149b1b5f9 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -253,13 +253,19 @@ impl GlobalState { | |||
253 | self.analysis_host.collect_garbage() | 253 | self.analysis_host.collect_garbage() |
254 | } | 254 | } |
255 | 255 | ||
256 | pub(crate) fn complete_request(&mut self, request: RequestMetrics) { | ||
257 | self.latest_requests.write().record(request) | ||
258 | } | ||
259 | |||
260 | pub(crate) fn send(&mut self, message: lsp_server::Message) { | 256 | pub(crate) fn send(&mut self, message: lsp_server::Message) { |
261 | self.sender.send(message).unwrap() | 257 | self.sender.send(message).unwrap() |
262 | } | 258 | } |
259 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { | ||
260 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { | ||
261 | let duration = start.elapsed(); | ||
262 | log::info!("handled req#{} in {:?}", response.id, duration); | ||
263 | let metrics = | ||
264 | RequestMetrics { id: response.id.clone(), method: method.to_string(), duration }; | ||
265 | self.latest_requests.write().record(metrics); | ||
266 | self.send(response.into()); | ||
267 | } | ||
268 | } | ||
263 | pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { | 269 | pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { |
264 | show_message(typ, message, &self.sender) | 270 | show_message(typ, message, &self.sender) |
265 | } | 271 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index b2ff9a157..12b494496 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -31,7 +31,6 @@ use stdx::{format_to, split_delim}; | |||
31 | use crate::{ | 31 | use crate::{ |
32 | cargo_target_spec::CargoTargetSpec, | 32 | cargo_target_spec::CargoTargetSpec, |
33 | config::RustfmtConfig, | 33 | config::RustfmtConfig, |
34 | diagnostics::DiagnosticTask, | ||
35 | from_json, from_proto, | 34 | from_json, from_proto, |
36 | global_state::GlobalStateSnapshot, | 35 | global_state::GlobalStateSnapshot, |
37 | lsp_ext::{self, InlayHint, InlayHintsParams}, | 36 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
@@ -950,7 +949,7 @@ pub(crate) fn handle_ssr( | |||
950 | pub(crate) fn publish_diagnostics( | 949 | pub(crate) fn publish_diagnostics( |
951 | snap: &GlobalStateSnapshot, | 950 | snap: &GlobalStateSnapshot, |
952 | file_id: FileId, | 951 | file_id: FileId, |
953 | ) -> Result<DiagnosticTask> { | 952 | ) -> Result<Vec<Diagnostic>> { |
954 | let _p = profile("publish_diagnostics"); | 953 | let _p = profile("publish_diagnostics"); |
955 | let line_index = snap.analysis.file_line_index(file_id)?; | 954 | let line_index = snap.analysis.file_line_index(file_id)?; |
956 | let diagnostics: Vec<Diagnostic> = snap | 955 | let diagnostics: Vec<Diagnostic> = snap |
@@ -967,7 +966,7 @@ pub(crate) fn publish_diagnostics( | |||
967 | tags: None, | 966 | tags: None, |
968 | }) | 967 | }) |
969 | .collect(); | 968 | .collect(); |
970 | Ok(DiagnosticTask::SetNative(file_id, diagnostics)) | 969 | Ok(diagnostics) |
971 | } | 970 | } |
972 | 971 | ||
973 | pub(crate) fn handle_inlay_hints( | 972 | pub(crate) fn handle_inlay_hints( |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c2f43df1d..1bd9d6389 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -15,7 +15,6 @@ use ra_project_model::{PackageRoot, ProjectWorkspace}; | |||
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | config::{Config, FilesWatcher, LinkedProject}, | 17 | config::{Config, FilesWatcher, LinkedProject}, |
18 | diagnostics::DiagnosticTask, | ||
19 | dispatch::{NotificationDispatcher, RequestDispatcher}, | 18 | dispatch::{NotificationDispatcher, RequestDispatcher}, |
20 | from_proto, | 19 | from_proto, |
21 | global_state::{file_id_to_url, GlobalState, Status}, | 20 | global_state::{file_id_to_url, GlobalState, Status}, |
@@ -23,7 +22,6 @@ use crate::{ | |||
23 | lsp_utils::{ | 22 | lsp_utils::{ |
24 | apply_document_changes, is_canceled, notification_is, notification_new, show_message, | 23 | apply_document_changes, is_canceled, notification_is, notification_new, show_message, |
25 | }, | 24 | }, |
26 | request_metrics::RequestMetrics, | ||
27 | Result, | 25 | Result, |
28 | }; | 26 | }; |
29 | 27 | ||
@@ -126,6 +124,52 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
126 | Ok(()) | 124 | Ok(()) |
127 | } | 125 | } |
128 | 126 | ||
127 | enum Event { | ||
128 | Lsp(lsp_server::Message), | ||
129 | Task(Task), | ||
130 | Vfs(vfs::loader::Message), | ||
131 | Flycheck(flycheck::Message), | ||
132 | } | ||
133 | |||
134 | #[derive(Debug)] | ||
135 | pub(crate) enum Task { | ||
136 | Response(Response), | ||
137 | Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), | ||
138 | Unit, | ||
139 | } | ||
140 | |||
141 | impl fmt::Debug for Event { | ||
142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
143 | let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { | ||
144 | f.debug_struct("Notification").field("method", ¬.method).finish() | ||
145 | }; | ||
146 | |||
147 | match self { | ||
148 | Event::Lsp(lsp_server::Message::Notification(not)) => { | ||
149 | if notification_is::<lsp_types::notification::DidOpenTextDocument>(not) | ||
150 | || notification_is::<lsp_types::notification::DidChangeTextDocument>(not) | ||
151 | { | ||
152 | return debug_verbose_not(not, f); | ||
153 | } | ||
154 | } | ||
155 | Event::Task(Task::Response(resp)) => { | ||
156 | return f | ||
157 | .debug_struct("Response") | ||
158 | .field("id", &resp.id) | ||
159 | .field("error", &resp.error) | ||
160 | .finish(); | ||
161 | } | ||
162 | _ => (), | ||
163 | } | ||
164 | match self { | ||
165 | Event::Lsp(it) => fmt::Debug::fmt(it, f), | ||
166 | Event::Task(it) => fmt::Debug::fmt(it, f), | ||
167 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | ||
168 | Event::Flycheck(it) => fmt::Debug::fmt(it, f), | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
129 | impl GlobalState { | 173 | impl GlobalState { |
130 | fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> { | 174 | fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> { |
131 | select! { | 175 | select! { |
@@ -145,101 +189,165 @@ impl GlobalState { | |||
145 | 189 | ||
146 | fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { | 190 | fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { |
147 | while let Some(event) = self.next_event(&inbox) { | 191 | while let Some(event) = self.next_event(&inbox) { |
148 | let loop_start = Instant::now(); | 192 | if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { |
149 | // NOTE: don't count blocking select! call as a loop-turn time | 193 | if not.method == lsp_types::notification::Exit::METHOD { |
150 | let _p = profile("main_loop_inner/loop-turn"); | 194 | return Ok(()); |
151 | 195 | } | |
152 | log::info!("loop turn = {:?}", event); | ||
153 | let queue_count = self.task_pool.0.len(); | ||
154 | if queue_count > 0 { | ||
155 | log::info!("queued count = {}", queue_count); | ||
156 | } | 196 | } |
197 | self.loop_turn(event)? | ||
198 | } | ||
199 | Err("client exited without proper shutdown sequence")? | ||
200 | } | ||
157 | 201 | ||
158 | let mut became_ready = false; | 202 | fn loop_turn(&mut self, event: Event) -> Result<()> { |
159 | match event { | 203 | let loop_start = Instant::now(); |
160 | Event::Lsp(msg) => match msg { | 204 | // NOTE: don't count blocking select! call as a loop-turn time |
161 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, | 205 | let _p = profile("main_loop_inner/loop-turn"); |
162 | lsp_server::Message::Notification(not) => { | 206 | |
163 | if not.method == lsp_types::notification::Exit::METHOD { | 207 | log::info!("loop turn = {:?}", event); |
164 | return Ok(()); | 208 | let queue_count = self.task_pool.0.len(); |
209 | if queue_count > 0 { | ||
210 | log::info!("queued count = {}", queue_count); | ||
211 | } | ||
212 | |||
213 | let mut became_ready = false; | ||
214 | match event { | ||
215 | Event::Lsp(msg) => match msg { | ||
216 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, | ||
217 | lsp_server::Message::Notification(not) => { | ||
218 | self.on_notification(not)?; | ||
219 | } | ||
220 | lsp_server::Message::Response(resp) => { | ||
221 | let handler = self.req_queue.outgoing.complete(resp.id.clone()); | ||
222 | handler(self, resp) | ||
223 | } | ||
224 | }, | ||
225 | Event::Task(task) => { | ||
226 | match task { | ||
227 | Task::Response(response) => self.respond(response), | ||
228 | Task::Diagnostics(diagnostics_per_file) => { | ||
229 | for (file_id, diagnostics) in diagnostics_per_file { | ||
230 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) | ||
165 | } | 231 | } |
166 | self.on_notification(not)?; | ||
167 | } | 232 | } |
168 | lsp_server::Message::Response(resp) => { | 233 | Task::Unit => (), |
169 | let handler = self.req_queue.outgoing.complete(resp.id.clone()); | ||
170 | handler(&mut self, resp) | ||
171 | } | ||
172 | }, | ||
173 | Event::Task(task) => { | ||
174 | self.on_task(task); | ||
175 | self.maybe_collect_garbage(); | ||
176 | } | 234 | } |
177 | Event::Vfs(task) => match task { | 235 | self.maybe_collect_garbage(); |
178 | vfs::loader::Message::Loaded { files } => { | 236 | } |
179 | let vfs = &mut self.vfs.write().0; | 237 | Event::Vfs(task) => match task { |
180 | for (path, contents) in files { | 238 | vfs::loader::Message::Loaded { files } => { |
181 | let path = VfsPath::from(path); | 239 | let vfs = &mut self.vfs.write().0; |
182 | if !self.mem_docs.contains(&path) { | 240 | for (path, contents) in files { |
183 | vfs.set_file_contents(path, contents) | 241 | let path = VfsPath::from(path); |
184 | } | 242 | if !self.mem_docs.contains(&path) { |
243 | vfs.set_file_contents(path, contents) | ||
185 | } | 244 | } |
186 | } | 245 | } |
187 | vfs::loader::Message::Progress { n_total, n_done } => { | 246 | } |
188 | let state = if n_done == 0 { | 247 | vfs::loader::Message::Progress { n_total, n_done } => { |
189 | Progress::Begin | 248 | let state = if n_done == 0 { |
190 | } else if n_done < n_total { | 249 | Progress::Begin |
191 | Progress::Report | 250 | } else if n_done < n_total { |
192 | } else { | 251 | Progress::Report |
193 | assert_eq!(n_done, n_total); | 252 | } else { |
194 | self.status = Status::Ready; | 253 | assert_eq!(n_done, n_total); |
195 | became_ready = true; | 254 | self.status = Status::Ready; |
196 | Progress::End | 255 | became_ready = true; |
256 | Progress::End | ||
257 | }; | ||
258 | report_progress( | ||
259 | self, | ||
260 | "roots scanned", | ||
261 | state, | ||
262 | Some(format!("{}/{}", n_done, n_total)), | ||
263 | Some(percentage(n_done, n_total)), | ||
264 | ) | ||
265 | } | ||
266 | }, | ||
267 | Event::Flycheck(task) => match task { | ||
268 | flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(), | ||
269 | |||
270 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { | ||
271 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( | ||
272 | &self.config.diagnostics, | ||
273 | &diagnostic, | ||
274 | &workspace_root, | ||
275 | ); | ||
276 | for diag in diagnostics { | ||
277 | let path = from_proto::vfs_path(&diag.location.uri)?; | ||
278 | let file_id = match self.vfs.read().0.file_id(&path) { | ||
279 | Some(file) => FileId(file.0), | ||
280 | None => { | ||
281 | log::error!( | ||
282 | "File with cargo diagnostic not found in VFS: {}", | ||
283 | path | ||
284 | ); | ||
285 | return Ok(()); | ||
286 | } | ||
197 | }; | 287 | }; |
198 | report_progress( | 288 | self.diagnostics.add_check_diagnostic(file_id, diag.diagnostic, diag.fixes) |
199 | &mut self, | ||
200 | "roots scanned", | ||
201 | state, | ||
202 | Some(format!("{}/{}", n_done, n_total)), | ||
203 | Some(percentage(n_done, n_total)), | ||
204 | ) | ||
205 | } | 289 | } |
206 | }, | 290 | } |
207 | Event::Flycheck(task) => on_check_task(task, &mut self)?, | ||
208 | } | ||
209 | 291 | ||
210 | let state_changed = self.process_changes(); | 292 | flycheck::Message::Progress(status) => { |
211 | if became_ready { | 293 | let (state, message) = match status { |
212 | if let Some(flycheck) = &self.flycheck { | 294 | flycheck::Progress::Being => (Progress::Begin, None), |
213 | flycheck.0.update(); | 295 | flycheck::Progress::DidCheckCrate(target) => { |
296 | (Progress::Report, Some(target)) | ||
297 | } | ||
298 | flycheck::Progress::End => (Progress::End, None), | ||
299 | }; | ||
300 | |||
301 | report_progress(self, "cargo check", state, message, None); | ||
214 | } | 302 | } |
303 | }, | ||
304 | } | ||
305 | |||
306 | let state_changed = self.process_changes(); | ||
307 | if became_ready { | ||
308 | if let Some(flycheck) = &self.flycheck { | ||
309 | flycheck.0.update(); | ||
215 | } | 310 | } |
311 | } | ||
216 | 312 | ||
217 | if self.status == Status::Ready && (state_changed || became_ready) { | 313 | if self.status == Status::Ready && (state_changed || became_ready) { |
218 | let subscriptions = self | 314 | let subscriptions = self |
219 | .mem_docs | 315 | .mem_docs |
220 | .iter() | 316 | .iter() |
221 | .map(|path| self.vfs.read().0.file_id(&path).unwrap()) | 317 | .map(|path| self.vfs.read().0.file_id(&path).unwrap()) |
222 | .collect::<Vec<_>>(); | 318 | .collect::<Vec<_>>(); |
319 | |||
320 | self.update_file_notifications_on_threadpool(subscriptions); | ||
321 | } | ||
223 | 322 | ||
224 | self.update_file_notifications_on_threadpool(subscriptions); | 323 | if let Some(diagnostic_changes) = self.diagnostics.take_changes() { |
324 | for file_id in diagnostic_changes { | ||
325 | let url = file_id_to_url(&self.vfs.read().0, file_id); | ||
326 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); | ||
327 | let params = | ||
328 | lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; | ||
329 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | ||
330 | self.send(not.into()); | ||
225 | } | 331 | } |
332 | } | ||
226 | 333 | ||
227 | let loop_duration = loop_start.elapsed(); | 334 | let loop_duration = loop_start.elapsed(); |
228 | if loop_duration > Duration::from_millis(100) { | 335 | if loop_duration > Duration::from_millis(100) { |
229 | log::error!("overly long loop turn: {:?}", loop_duration); | 336 | log::warn!("overly long loop turn: {:?}", loop_duration); |
230 | if env::var("RA_PROFILE").is_ok() { | 337 | if env::var("RA_PROFILE").is_ok() { |
231 | self.show_message( | 338 | self.show_message( |
232 | lsp_types::MessageType::Error, | 339 | lsp_types::MessageType::Error, |
233 | format!("overly long loop turn: {:?}", loop_duration), | 340 | format!("overly long loop turn: {:?}", loop_duration), |
234 | ) | 341 | ) |
235 | } | ||
236 | } | 342 | } |
237 | } | 343 | } |
238 | Err("client exited without proper shutdown sequence")? | 344 | Ok(()) |
239 | } | 345 | } |
240 | 346 | ||
241 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { | 347 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { |
242 | RequestDispatcher { req: Some(req), global_state: self, request_received } | 348 | self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); |
349 | |||
350 | RequestDispatcher { req: Some(req), global_state: self } | ||
243 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? | 351 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? |
244 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 352 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
245 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 353 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
@@ -400,27 +508,6 @@ impl GlobalState { | |||
400 | .finish(); | 508 | .finish(); |
401 | Ok(()) | 509 | Ok(()) |
402 | } | 510 | } |
403 | pub(crate) fn on_task(&mut self, task: Task) { | ||
404 | match task { | ||
405 | Task::Respond(response) => { | ||
406 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) | ||
407 | { | ||
408 | let duration = start.elapsed(); | ||
409 | log::info!("handled req#{} in {:?}", response.id, duration); | ||
410 | self.complete_request(RequestMetrics { | ||
411 | id: response.id.clone(), | ||
412 | method: method.to_string(), | ||
413 | duration, | ||
414 | }); | ||
415 | self.send(response.into()); | ||
416 | } | ||
417 | } | ||
418 | Task::Diagnostics(tasks) => { | ||
419 | tasks.into_iter().for_each(|task| on_diagnostic_task(task, self)) | ||
420 | } | ||
421 | Task::Unit => (), | ||
422 | } | ||
423 | } | ||
424 | fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) { | 511 | fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) { |
425 | log::trace!("updating notifications for {:?}", subscriptions); | 512 | log::trace!("updating notifications for {:?}", subscriptions); |
426 | if self.config.publish_diagnostics { | 513 | if self.config.publish_diagnostics { |
@@ -438,6 +525,7 @@ impl GlobalState { | |||
438 | () | 525 | () |
439 | }) | 526 | }) |
440 | .ok() | 527 | .ok() |
528 | .map(|diags| (file_id, diags)) | ||
441 | }) | 529 | }) |
442 | .collect::<Vec<_>>(); | 530 | .collect::<Vec<_>>(); |
443 | Task::Diagnostics(diagnostics) | 531 | Task::Diagnostics(diagnostics) |
@@ -454,115 +542,10 @@ impl GlobalState { | |||
454 | } | 542 | } |
455 | } | 543 | } |
456 | 544 | ||
457 | #[derive(Debug)] | ||
458 | pub(crate) enum Task { | ||
459 | Respond(Response), | ||
460 | Diagnostics(Vec<DiagnosticTask>), | ||
461 | Unit, | ||
462 | } | ||
463 | |||
464 | enum Event { | ||
465 | Lsp(lsp_server::Message), | ||
466 | Task(Task), | ||
467 | Vfs(vfs::loader::Message), | ||
468 | Flycheck(flycheck::Message), | ||
469 | } | ||
470 | |||
471 | impl fmt::Debug for Event { | ||
472 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
473 | let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { | ||
474 | f.debug_struct("Notification").field("method", ¬.method).finish() | ||
475 | }; | ||
476 | |||
477 | match self { | ||
478 | Event::Lsp(lsp_server::Message::Notification(not)) => { | ||
479 | if notification_is::<lsp_types::notification::DidOpenTextDocument>(not) | ||
480 | || notification_is::<lsp_types::notification::DidChangeTextDocument>(not) | ||
481 | { | ||
482 | return debug_verbose_not(not, f); | ||
483 | } | ||
484 | } | ||
485 | Event::Task(Task::Respond(resp)) => { | ||
486 | return f | ||
487 | .debug_struct("Response") | ||
488 | .field("id", &resp.id) | ||
489 | .field("error", &resp.error) | ||
490 | .finish(); | ||
491 | } | ||
492 | _ => (), | ||
493 | } | ||
494 | match self { | ||
495 | Event::Lsp(it) => fmt::Debug::fmt(it, f), | ||
496 | Event::Task(it) => fmt::Debug::fmt(it, f), | ||
497 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | ||
498 | Event::Flycheck(it) => fmt::Debug::fmt(it, f), | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | pub(crate) type ReqHandler = fn(&mut GlobalState, Response); | 545 | pub(crate) type ReqHandler = fn(&mut GlobalState, Response); |
504 | pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>; | 546 | pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; |
505 | const DO_NOTHING: ReqHandler = |_, _| (); | 547 | const DO_NOTHING: ReqHandler = |_, _| (); |
506 | 548 | ||
507 | fn on_check_task(task: flycheck::Message, global_state: &mut GlobalState) -> Result<()> { | ||
508 | match task { | ||
509 | flycheck::Message::ClearDiagnostics => { | ||
510 | on_diagnostic_task(DiagnosticTask::ClearCheck, global_state) | ||
511 | } | ||
512 | |||
513 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { | ||
514 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( | ||
515 | &global_state.config.diagnostics, | ||
516 | &diagnostic, | ||
517 | &workspace_root, | ||
518 | ); | ||
519 | for diag in diagnostics { | ||
520 | let path = from_proto::vfs_path(&diag.location.uri)?; | ||
521 | let file_id = match global_state.vfs.read().0.file_id(&path) { | ||
522 | Some(file) => FileId(file.0), | ||
523 | None => { | ||
524 | log::error!("File with cargo diagnostic not found in VFS: {}", path); | ||
525 | return Ok(()); | ||
526 | } | ||
527 | }; | ||
528 | |||
529 | on_diagnostic_task( | ||
530 | DiagnosticTask::AddCheck( | ||
531 | file_id, | ||
532 | diag.diagnostic, | ||
533 | diag.fixes.into_iter().map(|it| it.into()).collect(), | ||
534 | ), | ||
535 | global_state, | ||
536 | ) | ||
537 | } | ||
538 | } | ||
539 | |||
540 | flycheck::Message::Progress(status) => { | ||
541 | let (state, message) = match status { | ||
542 | flycheck::Progress::Being => (Progress::Begin, None), | ||
543 | flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), | ||
544 | flycheck::Progress::End => (Progress::End, None), | ||
545 | }; | ||
546 | |||
547 | report_progress(global_state, "cargo check", state, message, None); | ||
548 | } | ||
549 | }; | ||
550 | |||
551 | Ok(()) | ||
552 | } | ||
553 | |||
554 | fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) { | ||
555 | let subscriptions = global_state.diagnostics.handle_task(task); | ||
556 | |||
557 | for file_id in subscriptions { | ||
558 | let url = file_id_to_url(&global_state.vfs.read().0, file_id); | ||
559 | let diagnostics = global_state.diagnostics.diagnostics_for(file_id).cloned().collect(); | ||
560 | let params = lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; | ||
561 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | ||
562 | global_state.send(not.into()); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | #[derive(Eq, PartialEq)] | 549 | #[derive(Eq, PartialEq)] |
567 | enum Progress { | 550 | enum Progress { |
568 | Begin, | 551 | Begin, |