aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/flycheck/src/lib.rs92
-rw-r--r--crates/rust-analyzer/src/global_state.rs50
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs85
-rw-r--r--crates/rust-analyzer/src/main_loop.rs115
-rw-r--r--crates/rust-analyzer/src/reload.rs44
6 files changed, 185 insertions, 203 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 4dcab7a61..92ec4f92e 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -7,7 +7,7 @@ use std::{
7 io::{self, BufReader}, 7 io::{self, BufReader},
8 path::PathBuf, 8 path::PathBuf,
9 process::{Command, Stdio}, 9 process::{Command, Stdio},
10 time::Instant, 10 time::Duration,
11}; 11};
12 12
13use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
@@ -74,9 +74,6 @@ impl FlycheckHandle {
74 74
75#[derive(Debug)] 75#[derive(Debug)]
76pub enum Message { 76pub enum Message {
77 /// Request a clearing of all cached diagnostics from the check watcher
78 ClearDiagnostics,
79
80 /// Request adding a diagnostic with fixes included to a file 77 /// Request adding a diagnostic with fixes included to a file
81 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, 78 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
82 79
@@ -86,9 +83,10 @@ pub enum Message {
86 83
87#[derive(Debug)] 84#[derive(Debug)]
88pub enum Progress { 85pub enum Progress {
89 Being, 86 DidStart,
90 DidCheckCrate(String), 87 DidCheckCrate(String),
91 End, 88 DidFinish,
89 DidCancel,
92} 90}
93 91
94struct Restart; 92struct Restart;
@@ -97,19 +95,18 @@ struct FlycheckActor {
97 sender: Box<dyn Fn(Message) + Send>, 95 sender: Box<dyn Fn(Message) + Send>,
98 config: FlycheckConfig, 96 config: FlycheckConfig,
99 workspace_root: PathBuf, 97 workspace_root: PathBuf,
100 last_update_req: Option<Instant>,
101 /// WatchThread exists to wrap around the communication needed to be able to 98 /// WatchThread exists to wrap around the communication needed to be able to
102 /// run `cargo check` without blocking. Currently the Rust standard library 99 /// run `cargo check` without blocking. Currently the Rust standard library
103 /// doesn't provide a way to read sub-process output without blocking, so we 100 /// doesn't provide a way to read sub-process output without blocking, so we
104 /// have to wrap sub-processes output handling in a thread and pass messages 101 /// have to wrap sub-processes output handling in a thread and pass messages
105 /// back over a channel. 102 /// back over a channel.
106 // XXX: drop order is significant 103 // XXX: drop order is significant
107 check_process: Option<(Receiver<CheckEvent>, jod_thread::JoinHandle)>, 104 check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>,
108} 105}
109 106
110enum Event { 107enum Event {
111 Restart(Restart), 108 Restart(Restart),
112 CheckEvent(Option<CheckEvent>), 109 CheckEvent(Option<cargo_metadata::Message>),
113} 110}
114 111
115impl FlycheckActor { 112impl FlycheckActor {
@@ -118,7 +115,7 @@ impl FlycheckActor {
118 config: FlycheckConfig, 115 config: FlycheckConfig,
119 workspace_root: PathBuf, 116 workspace_root: PathBuf,
120 ) -> FlycheckActor { 117 ) -> FlycheckActor {
121 FlycheckActor { sender, config, workspace_root, last_update_req: None, check_process: None } 118 FlycheckActor { sender, config, workspace_root, check_process: None }
122 } 119 }
123 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { 120 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
124 let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); 121 let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan);
@@ -128,65 +125,48 @@ impl FlycheckActor {
128 } 125 }
129 } 126 }
130 fn run(&mut self, inbox: Receiver<Restart>) { 127 fn run(&mut self, inbox: Receiver<Restart>) {
131 // If we rerun the thread, we need to discard the previous check results first
132 self.send(Message::ClearDiagnostics);
133 self.send(Message::Progress(Progress::End));
134
135 while let Some(event) = self.next_event(&inbox) { 128 while let Some(event) = self.next_event(&inbox) {
136 match event { 129 match event {
137 Event::Restart(Restart) => self.last_update_req = Some(Instant::now()), 130 Event::Restart(Restart) => {
131 while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
132 self.cancel_check_process();
133 self.check_process = Some(self.start_check_process());
134 self.send(Message::Progress(Progress::DidStart));
135 }
138 Event::CheckEvent(None) => { 136 Event::CheckEvent(None) => {
139 // Watcher finished, replace it with a never channel to 137 // Watcher finished, replace it with a never channel to
140 // avoid busy-waiting. 138 // avoid busy-waiting.
141 self.check_process = None; 139 assert!(self.check_process.take().is_some());
140 self.send(Message::Progress(Progress::DidFinish));
142 } 141 }
143 Event::CheckEvent(Some(event)) => match event { 142 Event::CheckEvent(Some(message)) => match message {
144 CheckEvent::Begin => { 143 cargo_metadata::Message::CompilerArtifact(msg) => {
145 self.send(Message::Progress(Progress::Being));
146 }
147
148 CheckEvent::End => {
149 self.send(Message::Progress(Progress::End));
150 }
151
152 CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => {
153 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); 144 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
154 } 145 }
155 146
156 CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { 147 cargo_metadata::Message::CompilerMessage(msg) => {
157 self.send(Message::AddDiagnostic { 148 self.send(Message::AddDiagnostic {
158 workspace_root: self.workspace_root.clone(), 149 workspace_root: self.workspace_root.clone(),
159 diagnostic: msg.message, 150 diagnostic: msg.message,
160 }); 151 });
161 } 152 }
162 153
163 CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) 154 cargo_metadata::Message::BuildScriptExecuted(_)
164 | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) 155 | cargo_metadata::Message::BuildFinished(_)
165 | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) 156 | cargo_metadata::Message::TextLine(_)
166 | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} 157 | cargo_metadata::Message::Unknown => {}
167 }, 158 },
168 } 159 }
169 if self.should_recheck() {
170 self.last_update_req = None;
171 self.send(Message::ClearDiagnostics);
172 self.restart_check_process();
173 }
174 } 160 }
161 // If we rerun the thread, we need to discard the previous check results first
162 self.cancel_check_process();
175 } 163 }
176 fn should_recheck(&mut self) -> bool { 164 fn cancel_check_process(&mut self) {
177 if let Some(_last_update_req) = &self.last_update_req { 165 if self.check_process.take().is_some() {
178 // We currently only request an update on save, as we need up to 166 self.send(Message::Progress(Progress::DidCancel));
179 // date source on disk for cargo check to do it's magic, so we
180 // don't really need to debounce the requests at this point.
181 return true;
182 } 167 }
183 false
184 } 168 }
185 169 fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) {
186 fn restart_check_process(&mut self) {
187 // First, clear and cancel the old thread
188 self.check_process = None;
189
190 let mut cmd = match &self.config { 170 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { 171 FlycheckConfig::CargoCommand {
192 command, 172 command,
@@ -223,8 +203,6 @@ impl FlycheckActor {
223 let thread = jod_thread::spawn(move || { 203 let thread = jod_thread::spawn(move || {
224 // If we trigger an error here, we will do so in the loop instead, 204 // If we trigger an error here, we will do so in the loop instead,
225 // which will break out of the loop, and continue the shutdown 205 // which will break out of the loop, and continue the shutdown
226 let _ = message_send.send(CheckEvent::Begin);
227
228 let res = run_cargo(cmd, &mut |message| { 206 let res = run_cargo(cmd, &mut |message| {
229 // Skip certain kinds of messages to only spend time on what's useful 207 // Skip certain kinds of messages to only spend time on what's useful
230 match &message { 208 match &message {
@@ -237,7 +215,7 @@ impl FlycheckActor {
237 } 215 }
238 216
239 // if the send channel was closed, we want to shutdown 217 // if the send channel was closed, we want to shutdown
240 message_send.send(CheckEvent::Msg(message)).is_ok() 218 message_send.send(message).is_ok()
241 }); 219 });
242 220
243 if let Err(err) = res { 221 if let Err(err) = res {
@@ -245,12 +223,8 @@ impl FlycheckActor {
245 // to display user-caused misconfiguration errors instead of just logging them here 223 // to display user-caused misconfiguration errors instead of just logging them here
246 log::error!("Cargo watcher failed {:?}", err); 224 log::error!("Cargo watcher failed {:?}", err);
247 } 225 }
248
249 // We can ignore any error here, as we are already in the progress
250 // of shutting down.
251 let _ = message_send.send(CheckEvent::End);
252 }); 226 });
253 self.check_process = Some((message_recv, thread)) 227 (message_recv, thread)
254 } 228 }
255 229
256 fn send(&self, check_task: Message) { 230 fn send(&self, check_task: Message) {
@@ -258,12 +232,6 @@ impl FlycheckActor {
258 } 232 }
259} 233}
260 234
261enum CheckEvent {
262 Begin,
263 Msg(cargo_metadata::Message),
264 End,
265}
266
267fn run_cargo( 235fn run_cargo(
268 mut command: Command, 236 mut command: Command,
269 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, 237 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index f224c69e7..b8aa1e5b5 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -12,6 +12,7 @@ use parking_lot::RwLock;
12use ra_db::{CrateId, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use rustc_hash::{FxHashMap, FxHashSet};
15 16
16use crate::{ 17use crate::{
17 config::Config, 18 config::Config,
@@ -21,12 +22,10 @@ use crate::{
21 main_loop::Task, 22 main_loop::Task,
22 reload::SourceRootConfig, 23 reload::SourceRootConfig,
23 request_metrics::{LatestRequests, RequestMetrics}, 24 request_metrics::{LatestRequests, RequestMetrics},
24 show_message,
25 thread_pool::TaskPool, 25 thread_pool::TaskPool,
26 to_proto::url_from_abs_path, 26 to_proto::url_from_abs_path,
27 Result, 27 Result,
28}; 28};
29use rustc_hash::{FxHashMap, FxHashSet};
30 29
31#[derive(Eq, PartialEq)] 30#[derive(Eq, PartialEq)]
32pub(crate) enum Status { 31pub(crate) enum Status {
@@ -58,6 +57,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
58/// Note that this struct has more than on impl in various modules! 57/// Note that this struct has more than on impl in various modules!
59pub(crate) struct GlobalState { 58pub(crate) struct GlobalState {
60 sender: Sender<lsp_server::Message>, 59 sender: Sender<lsp_server::Message>,
60 req_queue: ReqQueue,
61 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, 61 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
62 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, 62 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
63 pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, 63 pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>,
@@ -67,7 +67,6 @@ pub(crate) struct GlobalState {
67 pub(crate) mem_docs: FxHashSet<VfsPath>, 67 pub(crate) mem_docs: FxHashSet<VfsPath>,
68 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 68 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
69 pub(crate) status: Status, 69 pub(crate) status: Status,
70 pub(crate) req_queue: ReqQueue,
71 pub(crate) source_root_config: SourceRootConfig, 70 pub(crate) source_root_config: SourceRootConfig,
72 pub(crate) proc_macro_client: ProcMacroClient, 71 pub(crate) proc_macro_client: ProcMacroClient,
73 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 72 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
@@ -103,16 +102,16 @@ impl GlobalState {
103 let analysis_host = AnalysisHost::new(config.lru_capacity); 102 let analysis_host = AnalysisHost::new(config.lru_capacity);
104 GlobalState { 103 GlobalState {
105 sender, 104 sender,
105 req_queue: ReqQueue::default(),
106 task_pool, 106 task_pool,
107 loader, 107 loader,
108 flycheck: None,
108 config, 109 config,
109 analysis_host, 110 analysis_host,
110 flycheck: None,
111 diagnostics: Default::default(), 111 diagnostics: Default::default(),
112 mem_docs: FxHashSet::default(), 112 mem_docs: FxHashSet::default(),
113 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 113 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
114 status: Status::default(), 114 status: Status::default(),
115 req_queue: ReqQueue::default(),
116 source_root_config: SourceRootConfig::default(), 115 source_root_config: SourceRootConfig::default(),
117 proc_macro_client: ProcMacroClient::dummy(), 116 proc_macro_client: ProcMacroClient::dummy(),
118 workspaces: Arc::new(Vec::new()), 117 workspaces: Arc::new(Vec::new()),
@@ -169,8 +168,35 @@ impl GlobalState {
169 } 168 }
170 } 169 }
171 170
172 pub(crate) fn send(&mut self, message: lsp_server::Message) { 171 pub(crate) fn send_request<R: lsp_types::request::Request>(
173 self.sender.send(message).unwrap() 172 &mut self,
173 params: R::Params,
174 handler: ReqHandler,
175 ) {
176 let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
177 self.send(request.into());
178 }
179 pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
180 let handler = self.req_queue.outgoing.complete(response.id.clone());
181 handler(self, response)
182 }
183
184 pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
185 &mut self,
186 params: N::Params,
187 ) {
188 let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
189 self.send(not.into());
190 }
191
192 pub(crate) fn register_request(
193 &mut self,
194 request: &lsp_server::Request,
195 request_received: Instant,
196 ) {
197 self.req_queue
198 .incoming
199 .register(request.id.clone(), (request.method.clone(), request_received));
174 } 200 }
175 pub(crate) fn respond(&mut self, response: lsp_server::Response) { 201 pub(crate) fn respond(&mut self, response: lsp_server::Response) {
176 if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { 202 if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
@@ -182,8 +208,14 @@ impl GlobalState {
182 self.send(response.into()); 208 self.send(response.into());
183 } 209 }
184 } 210 }
185 pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) { 211 pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
186 show_message(typ, message, &self.sender) 212 if let Some(response) = self.req_queue.incoming.cancel(request_id) {
213 self.send(response.into());
214 }
215 }
216
217 fn send(&mut self, message: lsp_server::Message) {
218 self.sender.send(message).unwrap()
187 } 219 }
188} 220}
189 221
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index a24dfe58c..407944d85 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -39,7 +39,7 @@ pub mod config;
39use serde::de::DeserializeOwned; 39use serde::de::DeserializeOwned;
40 40
41pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; 41pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
42pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; 42pub use crate::{caps::server_capabilities, main_loop::main_loop};
43use std::fmt; 43use std::fmt;
44 44
45pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 45pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 35917030c..0bc3ff115 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,24 +1,11 @@
1//! Utilities for LSP-related boilerplate code. 1//! Utilities for LSP-related boilerplate code.
2use std::{error::Error, ops::Range}; 2use std::{error::Error, ops::Range};
3 3
4use crossbeam_channel::Sender; 4use lsp_server::Notification;
5use lsp_server::{Message, Notification};
6use ra_db::Canceled; 5use ra_db::Canceled;
7use ra_ide::LineIndex; 6use ra_ide::LineIndex;
8use serde::Serialize;
9 7
10use crate::from_proto; 8use crate::{from_proto, global_state::GlobalState};
11
12pub fn show_message(
13 typ: lsp_types::MessageType,
14 message: impl Into<String>,
15 sender: &Sender<Message>,
16) {
17 let message = message.into();
18 let params = lsp_types::ShowMessageParams { typ, message };
19 let not = notification_new::<lsp_types::notification::ShowMessage>(params);
20 sender.send(not.into()).unwrap();
21}
22 9
23pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { 10pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
24 e.downcast_ref::<Canceled>().is_some() 11 e.downcast_ref::<Canceled>().is_some()
@@ -30,12 +17,68 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
30 notification.method == N::METHOD 17 notification.method == N::METHOD
31} 18}
32 19
33pub(crate) fn notification_new<N>(params: N::Params) -> Notification 20#[derive(Debug, Eq, PartialEq)]
34where 21pub(crate) enum Progress {
35 N: lsp_types::notification::Notification, 22 Begin,
36 N::Params: Serialize, 23 Report,
37{ 24 End,
38 Notification::new(N::METHOD.to_string(), params) 25}
26
27impl Progress {
28 pub(crate) fn percentage(done: usize, total: usize) -> f64 {
29 (done as f64 / total.max(1) as f64) * 100.0
30 }
31}
32
33impl GlobalState {
34 pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
35 let message = message.into();
36 self.send_notification::<lsp_types::notification::ShowMessage>(
37 lsp_types::ShowMessageParams { typ, message },
38 )
39 }
40
41 pub(crate) fn report_progress(
42 &mut self,
43 title: &str,
44 state: Progress,
45 message: Option<String>,
46 percentage: Option<f64>,
47 ) {
48 if !self.config.client_caps.work_done_progress {
49 return;
50 }
51 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
52 let work_done_progress = match state {
53 Progress::Begin => {
54 self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
55 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
56 |_, _| (),
57 );
58
59 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
60 title: title.into(),
61 cancellable: None,
62 message,
63 percentage,
64 })
65 }
66 Progress::Report => {
67 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
68 cancellable: None,
69 message,
70 percentage,
71 })
72 }
73 Progress::End => {
74 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
75 }
76 };
77 self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
78 token,
79 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
80 });
81 }
39} 82}
40 83
41pub(crate) fn apply_document_changes( 84pub(crate) fn apply_document_changes(
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8fc816cbd..e5194fe41 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -7,7 +7,7 @@ use std::{
7 7
8use crossbeam_channel::{never, select, Receiver}; 8use crossbeam_channel::{never, select, Receiver};
9use lsp_server::{Connection, Notification, Request, Response}; 9use lsp_server::{Connection, Notification, Request, Response};
10use lsp_types::{notification::Notification as _, request::Request as _}; 10use lsp_types::notification::Notification as _;
11use ra_db::VfsPath; 11use ra_db::VfsPath;
12use ra_ide::{Canceled, FileId}; 12use ra_ide::{Canceled, FileId};
13use ra_prof::profile; 13use ra_prof::profile;
@@ -18,7 +18,7 @@ use crate::{
18 from_proto, 18 from_proto,
19 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 19 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
20 handlers, lsp_ext, 20 handlers, lsp_ext,
21 lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new}, 21 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
22 Result, 22 Result,
23}; 23};
24 24
@@ -143,10 +143,7 @@ impl GlobalState {
143 lsp_server::Message::Notification(not) => { 143 lsp_server::Message::Notification(not) => {
144 self.on_notification(not)?; 144 self.on_notification(not)?;
145 } 145 }
146 lsp_server::Message::Response(resp) => { 146 lsp_server::Message::Response(resp) => self.complete_request(resp),
147 let handler = self.req_queue.outgoing.complete(resp.id.clone());
148 handler(self, resp)
149 }
150 }, 147 },
151 Event::Task(task) => { 148 Event::Task(task) => {
152 match task { 149 match task {
@@ -181,18 +178,15 @@ impl GlobalState {
181 became_ready = true; 178 became_ready = true;
182 Progress::End 179 Progress::End
183 }; 180 };
184 report_progress( 181 self.report_progress(
185 self,
186 "roots scanned", 182 "roots scanned",
187 state, 183 state,
188 Some(format!("{}/{}", n_done, n_total)), 184 Some(format!("{}/{}", n_done, n_total)),
189 Some(percentage(n_done, n_total)), 185 Some(Progress::percentage(n_done, n_total)),
190 ) 186 )
191 } 187 }
192 }, 188 },
193 Event::Flycheck(task) => match task { 189 Event::Flycheck(task) => match task {
194 flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(),
195
196 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 190 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
197 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 191 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
198 &self.config.diagnostics, 192 &self.config.diagnostics,
@@ -215,14 +209,19 @@ impl GlobalState {
215 209
216 flycheck::Message::Progress(status) => { 210 flycheck::Message::Progress(status) => {
217 let (state, message) = match status { 211 let (state, message) = match status {
218 flycheck::Progress::Being => (Progress::Begin, None), 212 flycheck::Progress::DidStart => {
213 self.diagnostics.clear_check();
214 (Progress::Begin, None)
215 }
219 flycheck::Progress::DidCheckCrate(target) => { 216 flycheck::Progress::DidCheckCrate(target) => {
220 (Progress::Report, Some(target)) 217 (Progress::Report, Some(target))
221 } 218 }
222 flycheck::Progress::End => (Progress::End, None), 219 flycheck::Progress::DidFinish | flycheck::Progress::DidCancel => {
220 (Progress::End, None)
221 }
223 }; 222 };
224 223
225 report_progress(self, "cargo check", state, message, None); 224 self.report_progress("cargo check", state, message, None);
226 } 225 }
227 }, 226 },
228 } 227 }
@@ -248,10 +247,9 @@ impl GlobalState {
248 for file_id in diagnostic_changes { 247 for file_id in diagnostic_changes {
249 let url = file_id_to_url(&self.vfs.read().0, file_id); 248 let url = file_id_to_url(&self.vfs.read().0, file_id);
250 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); 249 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
251 let params = 250 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
252 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; 251 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None },
253 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 252 );
254 self.send(not.into());
255 } 253 }
256 } 254 }
257 255
@@ -269,7 +267,7 @@ impl GlobalState {
269 } 267 }
270 268
271 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { 269 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
272 self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); 270 self.register_request(&req, request_received);
273 271
274 RequestDispatcher { req: Some(req), global_state: self } 272 RequestDispatcher { req: Some(req), global_state: self }
275 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? 273 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))?
@@ -333,9 +331,7 @@ impl GlobalState {
333 lsp_types::NumberOrString::Number(id) => id.into(), 331 lsp_types::NumberOrString::Number(id) => id.into(),
334 lsp_types::NumberOrString::String(id) => id.into(), 332 lsp_types::NumberOrString::String(id) => id.into(),
335 }; 333 };
336 if let Some(response) = this.req_queue.incoming.cancel(id) { 334 this.cancel(id);
337 this.send(response.into());
338 }
339 Ok(()) 335 Ok(())
340 })? 336 })?
341 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { 337 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
@@ -370,13 +366,13 @@ impl GlobalState {
370 this.loader.handle.invalidate(path.to_path_buf()); 366 this.loader.handle.invalidate(path.to_path_buf());
371 } 367 }
372 } 368 }
373 let params = lsp_types::PublishDiagnosticsParams { 369 this.send_notification::<lsp_types::notification::PublishDiagnostics>(
374 uri: params.text_document.uri, 370 lsp_types::PublishDiagnosticsParams {
375 diagnostics: Vec::new(), 371 uri: params.text_document.uri,
376 version: None, 372 diagnostics: Vec::new(),
377 }; 373 version: None,
378 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 374 },
379 this.send(not.into()); 375 );
380 Ok(()) 376 Ok(())
381 })? 377 })?
382 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { 378 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
@@ -388,8 +384,7 @@ impl GlobalState {
388 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { 384 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
389 // As stated in https://github.com/microsoft/language-server-protocol/issues/676, 385 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
390 // this notification's parameters should be ignored and the actual config queried separately. 386 // this notification's parameters should be ignored and the actual config queried separately.
391 let request = this.req_queue.outgoing.register( 387 this.send_request::<lsp_types::request::WorkspaceConfiguration>(
392 lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
393 lsp_types::ConfigurationParams { 388 lsp_types::ConfigurationParams {
394 items: vec![lsp_types::ConfigurationItem { 389 items: vec![lsp_types::ConfigurationItem {
395 scope_uri: None, 390 scope_uri: None,
@@ -417,7 +412,6 @@ impl GlobalState {
417 } 412 }
418 }, 413 },
419 ); 414 );
420 this.send(request.into());
421 415
422 return Ok(()); 416 return Ok(());
423 })? 417 })?
@@ -465,60 +459,3 @@ impl GlobalState {
465 }); 459 });
466 } 460 }
467} 461}
468
469#[derive(Eq, PartialEq)]
470enum Progress {
471 Begin,
472 Report,
473 End,
474}
475
476fn percentage(done: usize, total: usize) -> f64 {
477 (done as f64 / total.max(1) as f64) * 100.0
478}
479
480fn report_progress(
481 global_state: &mut GlobalState,
482 title: &str,
483 state: Progress,
484 message: Option<String>,
485 percentage: Option<f64>,
486) {
487 if !global_state.config.client_caps.work_done_progress {
488 return;
489 }
490 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
491 let work_done_progress = match state {
492 Progress::Begin => {
493 let work_done_progress_create = global_state.req_queue.outgoing.register(
494 lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
495 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
496 |_, _| (),
497 );
498 global_state.send(work_done_progress_create.into());
499
500 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
501 title: title.into(),
502 cancellable: None,
503 message,
504 percentage,
505 })
506 }
507 Progress::Report => {
508 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
509 cancellable: None,
510 message,
511 percentage,
512 })
513 }
514 Progress::End => {
515 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
516 }
517 };
518 let notification =
519 notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
520 token,
521 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
522 });
523 global_state.send(notification.into());
524}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index a22d3e262..ec71f8b29 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -1,9 +1,8 @@
1//! Project loading & configuration updates 1//! Project loading & configuration updates
2use std::sync::Arc; 2use std::{mem, sync::Arc};
3 3
4use crossbeam_channel::unbounded; 4use crossbeam_channel::unbounded;
5use flycheck::FlycheckHandle; 5use flycheck::FlycheckHandle;
6use lsp_types::request::Request;
7use ra_db::{CrateGraph, SourceRoot, VfsPath}; 6use ra_db::{CrateGraph, SourceRoot, VfsPath};
8use ra_ide::AnalysisChange; 7use ra_ide::AnalysisChange;
9use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; 8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
@@ -15,12 +14,14 @@ use crate::{
15}; 14};
16 15
17impl GlobalState { 16impl GlobalState {
18 pub(crate) fn update_configuration(&mut self, new_config: Config) { 17 pub(crate) fn update_configuration(&mut self, config: Config) {
19 self.analysis_host.update_lru_capacity(new_config.lru_capacity); 18 let old_config = mem::replace(&mut self.config, config);
20 if new_config.flycheck != self.config.flycheck { 19 if self.config.lru_capacity != old_config.lru_capacity {
20 self.analysis_host.update_lru_capacity(old_config.lru_capacity);
21 }
22 if self.config.flycheck != old_config.flycheck {
21 self.reload_flycheck(); 23 self.reload_flycheck();
22 } 24 }
23 self.config = new_config;
24 } 25 }
25 pub(crate) fn reload(&mut self) { 26 pub(crate) fn reload(&mut self) {
26 let workspaces = { 27 let workspaces = {
@@ -36,27 +37,31 @@ impl GlobalState {
36 self.config 37 self.config
37 .linked_projects 38 .linked_projects
38 .iter() 39 .iter()
39 .filter_map(|project| match project { 40 .map(|project| match project {
40 LinkedProject::ProjectManifest(manifest) => { 41 LinkedProject::ProjectManifest(manifest) => {
41 ra_project_model::ProjectWorkspace::load( 42 ra_project_model::ProjectWorkspace::load(
42 manifest.clone(), 43 manifest.clone(),
43 &self.config.cargo, 44 &self.config.cargo,
44 self.config.with_sysroot, 45 self.config.with_sysroot,
45 ) 46 )
46 .map_err(|err| {
47 log::error!("failed to load workspace: {:#}", err);
48 self.show_message(
49 lsp_types::MessageType::Error,
50 format!("rust-analyzer failed to load workspace: {:#}", err),
51 );
52 })
53 .ok()
54 } 47 }
55 LinkedProject::InlineJsonProject(it) => { 48 LinkedProject::InlineJsonProject(it) => {
56 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) 49 Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
57 } 50 }
58 }) 51 })
59 .collect::<Vec<_>>() 52 .collect::<Vec<_>>()
53 .into_iter()
54 .filter_map(|res| {
55 res.map_err(|err| {
56 log::error!("failed to load workspace: {:#}", err);
57 self.show_message(
58 lsp_types::MessageType::Error,
59 format!("rust-analyzer failed to load workspace: {:#}", err),
60 );
61 })
62 .ok()
63 })
64 .collect::<Vec<_>>()
60 }; 65 };
61 66
62 if let FilesWatcher::Client = self.config.files.watcher { 67 if let FilesWatcher::Client = self.config.files.watcher {
@@ -74,13 +79,10 @@ impl GlobalState {
74 method: "workspace/didChangeWatchedFiles".to_string(), 79 method: "workspace/didChangeWatchedFiles".to_string(),
75 register_options: Some(serde_json::to_value(registration_options).unwrap()), 80 register_options: Some(serde_json::to_value(registration_options).unwrap()),
76 }; 81 };
77 let params = lsp_types::RegistrationParams { registrations: vec![registration] }; 82 self.send_request::<lsp_types::request::RegisterCapability>(
78 let request = self.req_queue.outgoing.register( 83 lsp_types::RegistrationParams { registrations: vec![registration] },
79 lsp_types::request::RegisterCapability::METHOD.to_string(),
80 params,
81 |_, _| (), 84 |_, _| (),
82 ); 85 );
83 self.send(request.into());
84 } 86 }
85 87
86 let mut change = AnalysisChange::new(); 88 let mut change = AnalysisChange::new();