Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/aft/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5442,7 +5442,7 @@ impl AppContext {
pub fn lsp_notify_file_changed(&self, file_path: &Path, content: &str) {
let config = self.config();
if let Some(mut lsp) = self.lsp_manager.try_lock() {
if let Err(e) = lsp.notify_file_changed(file_path, content, &config) {
if let Err(e) = lsp.notify_file_changed_if_running(file_path, content, &config) {
crate::slog_warn!("sync error for {}: {}", file_path.display(), e);
}
}
Expand Down
60 changes: 60 additions & 0 deletions crates/aft/src/lsp/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,23 @@ impl LspManager {
.successful
}

fn running_server_keys_for_file(&self, file_path: &Path, config: &Config) -> Vec<ServerKey> {
let mut keys = Vec::new();
for def in servers_for_file(file_path, config) {
let Some(root) = def.workspace_root_for_file(file_path) else {
continue;
};
let key = ServerKey {
kind: def.kind.clone(),
root,
};
if self.clients.contains_key(&key) {
keys.push(key);
}
}
keys
}

/// Detailed version of [`ensure_server_for_file`] that records every
/// matching server's outcome (`Ok` / `NoRootMarker` / `BinaryNotInstalled`
/// / `SpawnFailed`).
Expand Down Expand Up @@ -628,6 +645,32 @@ impl LspManager {
) -> Result<Vec<(ServerKey, i32)>, LspError> {
let canonical_path = canonicalize_for_lsp(file_path)?;
let server_keys = self.ensure_server_for_file(&canonical_path, config);
self.notify_file_changed_for_server_keys(canonical_path, content, server_keys)
}

/// Notify only LSP servers that are already running for this file.
///
/// Post-write notifications are best-effort and must not make a mutation
/// wait for cold server startup. Explicit LSP requests use
/// [`Self::notify_file_changed_versioned`] and retain lazy startup.
pub fn notify_file_changed_if_running(
&mut self,
file_path: &Path,
content: &str,
config: &Config,
) -> Result<(), LspError> {
let canonical_path = canonicalize_for_lsp(file_path)?;
let server_keys = self.running_server_keys_for_file(&canonical_path, config);
self.notify_file_changed_for_server_keys(canonical_path, content, server_keys)
.map(|_| ())
}

fn notify_file_changed_for_server_keys(
&mut self,
canonical_path: PathBuf,
content: &str,
server_keys: Vec<ServerKey>,
) -> Result<Vec<(ServerKey, i32)>, LspError> {
if server_keys.is_empty() {
return Ok(Vec::new());
}
Expand Down Expand Up @@ -2153,7 +2196,10 @@ mod failure_hint_tests {

#[cfg(test)]
mod diagnostic_capacity_tests {
use std::fs;

use super::LspManager;
use crate::config::Config;

// The lsp.diagnostic_cache_size config knob must actually take effect:
// set_diagnostic_capacity (called at AppContext construction with the config
Expand All @@ -2178,6 +2224,20 @@ mod diagnostic_capacity_tests {
assert_eq!(manager.clear_failed_spawns(), 1);
assert_eq!(manager.clear_failed_spawns(), 0);
}

#[test]
fn post_write_notification_does_not_start_a_cold_server() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("main.ts");
fs::write(dir.path().join("package.json"), "{}").unwrap();
fs::write(&file, "export const value = 1;\n").unwrap();

let mut manager = LspManager::new();
manager
.notify_file_changed_if_running(&file, "export const value = 1;\n", &Config::default())
.unwrap();
assert!(manager.clients.is_empty());
}
}

#[cfg(test)]
Expand Down
Loading