Long-lived background task that runs PHPCS on pending files. Spawned once during `initialized`, alongside the main diagnostic worker and the PHPStan worker. This task is completely independent: native diagnostics and PHPStan are never blocked. ## Serialization guarantee At most one PHPCS process runs at a time. The worker loop: 1. Wait for a notification (new edit arrived). 2. Debounce: slee
(&self)
| 1296 | /// notification and loops back to step 1, starting a fresh run |
| 1297 | /// with the latest content. |
| 1298 | pub(crate) async fn phpcs_worker(&self) { |
| 1299 | loop { |
| 1300 | if self.shutdown_flag.load(Ordering::Acquire) { |
| 1301 | return; |
| 1302 | } |
| 1303 | |
| 1304 | // ── Step 1: wait for work ─────────────────────────────── |
| 1305 | self.phpcs_notify.notified().await; |
| 1306 | |
| 1307 | if self.shutdown_flag.load(Ordering::Acquire) { |
| 1308 | return; |
| 1309 | } |
| 1310 | |
| 1311 | // Drain any extra stored permits (same rationale as the |
| 1312 | // PHPStan worker). |
| 1313 | let _ = |
| 1314 | tokio::time::timeout(std::time::Duration::ZERO, self.phpcs_notify.notified()).await; |
| 1315 | |
| 1316 | // ── Step 2: debounce ──────────────────────────────────── |
| 1317 | loop { |
| 1318 | let version_before = self.diag_version.load(Ordering::Acquire); |
| 1319 | tokio::time::sleep(std::time::Duration::from_millis(PHPCS_DEBOUNCE_MS)).await; |
| 1320 | let version_after = self.diag_version.load(Ordering::Acquire); |
| 1321 | if version_before == version_after { |
| 1322 | break; |
| 1323 | } |
| 1324 | // More edits arrived — loop and debounce again. |
| 1325 | } |
| 1326 | |
| 1327 | // ── Step 3: snapshot the pending URI ──────────────────── |
| 1328 | let uri = { |
| 1329 | let mut pending = self.phpcs_pending_uri.lock(); |
| 1330 | pending.take() |
| 1331 | }; |
| 1332 | let uri = match uri { |
| 1333 | Some(u) => u, |
| 1334 | None => continue, |
| 1335 | }; |
| 1336 | |
| 1337 | // Snapshot the file content. |
| 1338 | let content = { |
| 1339 | let files = self.open_files.read(); |
| 1340 | match files.get(&uri) { |
| 1341 | Some(c) => c.clone(), |
| 1342 | None => continue, |
| 1343 | } |
| 1344 | }; |
| 1345 | |
| 1346 | // ── Step 4: resolve PHPCS binary ──────────────────────── |
| 1347 | let config = self.config(); |
| 1348 | if config.phpcs.is_disabled() { |
| 1349 | continue; |
| 1350 | } |
| 1351 | |
| 1352 | let file_path = match uri.parse::<Url>().ok().and_then(|u| u.to_file_path().ok()) { |
| 1353 | Some(p) => p, |
| 1354 | None => continue, |
| 1355 | }; |
no test coverage detected