fix: avoid redundant workspace reloads on startup#995
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces optimizations to the workspace reloading process by skipping reindexing when no non-standard local files are present and avoiding configuration reloads if the client configuration remains unchanged. It also adds unit tests to verify these behaviors. Feedback focuses on improving the idiomatic nature of the Rust code by avoiding side effects in iterator filters and ensuring tests use temporary directories for better isolation.
| .filter(|file_id| { | ||
| let is_non_std = !module_index.is_std(file_id); | ||
| had_existing_non_std_local_files |= is_non_std; | ||
| is_non_std | ||
| }) |
There was a problem hiding this comment.
Using a side-effect inside a filter closure makes the code less readable and harder to reason about. A more idiomatic and clearer approach would be to separate the check for existing files from the filtering logic. For example, you could use peekable() on the iterator to check if it's empty before proceeding to collect the stale URIs. This would make the intent of the code more explicit.
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn reload_workspace_files_skips_reindex_when_bootstrapping_workspace() { | ||
| let mut analysis = EmmyLuaAnalysis::new(); | ||
| let workspace_root = std::env::current_dir().unwrap(); | ||
| let file_path = workspace_root.join("__reload_workspace_startup_test.lua"); | ||
| analysis.add_main_workspace(workspace_root); | ||
|
|
||
| analysis.reload_workspace_files( | ||
| vec![(file_path.clone(), Some("return true\n".to_string()))], | ||
| Vec::new(), | ||
| ); | ||
|
|
||
| assert_eq!(analysis.reindex_count, 0); | ||
| assert!( | ||
| analysis | ||
| .get_file_id(&file_path_to_uri(&file_path).unwrap()) | ||
| .is_some() | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn reload_workspace_files_reindexes_existing_workspace_files() { | ||
| let mut analysis = EmmyLuaAnalysis::new(); | ||
| let workspace_root = std::env::current_dir().unwrap(); | ||
| let file_path = workspace_root.join("__reload_workspace_existing_test.lua"); | ||
| analysis.add_main_workspace(workspace_root); | ||
| analysis.update_files_by_path(vec![(file_path.clone(), Some("return true\n".to_string()))]); | ||
|
|
||
| analysis.reload_workspace_files( | ||
| vec![(file_path, Some("return false\n".to_string()))], | ||
| Vec::new(), | ||
| ); | ||
|
|
||
| assert_eq!(analysis.reindex_count, 1); | ||
| } | ||
| } |
There was a problem hiding this comment.
These tests create files in the current working directory and do not clean them up, which can pollute the development environment. It's best practice for tests to be self-contained. Please use a temporary directory for test files and ensure they are cleaned up. The tempfile crate is great for this and will handle cleanup automatically. You'll need to add tempfile to your [dev-dependencies].
mod tests {
use super::*;
#[test]
fn reload_workspace_files_skips_reindex_when_bootstrapping_workspace() {
let temp_dir = tempfile::tempdir().unwrap();
let workspace_root = temp_dir.path().to_path_buf();
let mut analysis = EmmyLuaAnalysis::new();
let file_path = workspace_root.join("__reload_workspace_startup_test.lua");
analysis.add_main_workspace(workspace_root);
analysis.reload_workspace_files(
vec![(file_path.clone(), Some("return true\n".to_string()))],
Vec::new(),
);
assert_eq!(analysis.reindex_count, 0);
assert!(
analysis
.get_file_id(&file_path_to_uri(&file_path).unwrap())
.is_some()
);
}
#[test]
fn reload_workspace_files_reindexes_existing_workspace_files() {
let temp_dir = tempfile::tempdir().unwrap();
let workspace_root = temp_dir.path().to_path_buf();
let mut analysis = EmmyLuaAnalysis::new();
let file_path = workspace_root.join("__reload_workspace_existing_test.lua");
analysis.add_main_workspace(workspace_root);
analysis.update_files_by_path(vec![(file_path.clone(), Some("return true\n".to_string()))]);
analysis.reload_workspace_files(
vec![(file_path, Some("return false\n".to_string()))],
Vec::new(),
);
assert_eq!(analysis.reindex_count, 1);
}
}Some clients send `workspace/didChangeConfiguration` during startup even when the effective configuration has not changed. The language server would fetch the config again and always schedule a full workspace reload, which rebuilt watch registrations and analysis state for no reason. Compare the newly fetched `ClientConfig` with the current value and skip the reload when they are identical. Keep a regression test around the configuration handler so unchanged notifications do not trigger watched-file re-registration.
`reload_workspace_files` was introduced to support full workspace reloads, but startup now also routes through that helper. Because the helper always called `reindex()` after `update_files_by_path(...)`, bootstrapping an empty workspace analyzed the same files twice. Only run the final reindex when non-stdlib local workspace files already exist. That preserves reload behavior for existing workspaces while skipping the extra startup pass.
6d86413 to
08ec37f
Compare
Summary
workspace/didChangeConfigurationnotifications when the effective client config is unchangedreload_workspace_filesTesting