aboutsummaryrefslogtreecommitdiff
path: root/crates/flycheck/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/flycheck/src/lib.rs')
-rw-r--r--crates/flycheck/src/lib.rs92
1 files changed, 30 insertions, 62 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,