Skip to content

Commit ff680df

Browse files
alanzmeta-codesync[bot]
authored andcommitted
Properly process changes to open configuration files
Summary: ELP as an LSP server operates in a dyamic environment, by design. The key thing ts must deal with is changes to files. A given project loaded into ELP has to process file-changes from two primarily different sources. The first is open files in the LSP client (VS Code), and these are sent via LSP protocol as they happen, as well as generating a `DidSaveTextDocument` notification when saved. The other is changes that happen on the filesystem directly, from any source, such as an external editor, version countrol operation, build, or the like. These are monitored via a file watcher, and the watching is also delegated to the LSP client. There are some wrinkles to this. We send a registration message to the client to watch all files of interest in a project, both source and configuration. So of necessity this will overlap with any files that are also open in the IDE. To avoid double processing these, we only process open files in the `DidSaveTextDocument` notification, and do not process open files in the `DidChangeWatchedFiles` notification. This is all very well, but at some point there was a rationalisation of change processing, with the realisation that we only care about `DidSaveTextDocument` notifications for triggering diagnostic requests for open files for diagnostic sources not providing on-change diagnostics. Which is now most of them. So we modified the registered scope for `DidSaveTextDocument` to only be `.erl`/`.hrl` files. This meant that we no longer processed changes to configuration files that are open in the IDE, which is the main way they pick up changes. ## This diff We restore the desired immediate reaction to changed files by never doing the check in the `DidSaveTextDocument` notification processing, since it never applied anyway, and so always doing it in `DidChangeWatchedFiles`. We also tidy up the registering of watched files by first unregistering, otherwise the client accumulates multiple watches for the same files, and sends a change notification for each registration it has. Even with this removal, we still get 2 notifications per file change, so add logic to de-dupe them while processing, as updating config can be an expensive operation. Reviewed By: jcpetruzza Differential Revision: D85956439 fbshipit-source-id: 961618711347d173596f69f5fd94a435b3fd45b1
1 parent 90530e3 commit ff680df

File tree

1 file changed

+36
-16
lines changed

1 file changed

+36
-16
lines changed

crates/elp/src/server.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -852,19 +852,14 @@ impl Server {
852852
if let Ok(path) = convert::abs_path(&change.uri) {
853853
// We only put .erl/.hrl files into the
854854
// `mem_docs` store, but will get changes for
855-
// other kinds of files. So we neede to do
855+
// other kinds of files. So we need to do
856856
// this check.
857+
// Note: we only receive DidSaveTextDocument notifications for files that pass the filter
858+
// in the TextDocumentRegistrationOptions setting at the beginning of main_loop.
857859
let opened = convert::vfs_path(&change.uri)
858860
.map(|vfs_path| this.mem_docs.read().contains(&vfs_path))
859861
.unwrap_or(false);
860862
if opened {
861-
if this.should_reload_config_for_path(&path) {
862-
// e.g. `.elp_lint.toml`
863-
this.refresh_config();
864-
}
865-
if this.should_reload_project_for_path(&path, &change) {
866-
this.reload_manager.lock().add(path.clone());
867-
}
868863
this.eqwalizer_and_erlang_service_diagnostics_requested = true;
869864
if this.config.eqwalizer().all {
870865
this.eqwalizer_project_diagnostics_requested = true;
@@ -881,22 +876,31 @@ impl Server {
881876
Ok(())
882877
})?
883878
.on::<notification::DidChangeWatchedFiles>(|this, params| {
879+
// For some reason, we are seeing duplicate notifications for the same file.
880+
// But we cannot use FxHashSet because change does not implement Hash.
884881
let changes: &[FileEvent] = &params.changes;
885882
let mut refresh_config = false;
883+
let mut seen: FxHashMap<Url,FileChangeType> = FxHashMap::default();
886884
for change in changes {
885+
if seen.get(&change.uri).is_some_and(|prev_typ| prev_typ == &change.typ) {
886+
continue;
887+
}
888+
seen.insert(change.uri.clone(), change.typ);
887889
if let Ok(path) = convert::abs_path(&change.uri) {
888890
let opened = convert::vfs_path(&change.uri)
889891
.map(|vfs_path| this.mem_docs.read().contains(&vfs_path))
890892
.unwrap_or(false);
891893
log::info!(target: FILE_WATCH_LOGGER_NAME, "DidChangeWatchedFiles:{}:{}", &opened, &path);
894+
// We process config changes here, because we do not register to receive
895+
// DidSaveTextDocument notifiactions for these kinds of files
896+
if this.should_reload_project_for_path(&path, change) {
897+
this.reload_manager.lock().add(path.clone());
898+
}
899+
if this.should_reload_config_for_path(&path) {
900+
// e.g. `.elp_lint.toml`
901+
refresh_config = true;
902+
}
892903
if !opened {
893-
if this.should_reload_project_for_path(&path, change) {
894-
this.reload_manager.lock().add(path.clone());
895-
}
896-
if this.should_reload_config_for_path(&path) {
897-
// e.g. `.elp_lint.toml`
898-
refresh_config = true;
899-
}
900904
this.vfs_loader.handle.invalidate(path);
901905
}
902906
}
@@ -1429,8 +1433,24 @@ impl Server {
14291433
watchers: folders.watch,
14301434
};
14311435

1436+
let registration_id = "workspace/didChangeWatchedFiles".to_string();
1437+
1438+
// Unregister the old watcher
1439+
let unregistrations = vec![lsp_types::Unregistration {
1440+
id: registration_id.clone(),
1441+
method: notification::DidChangeWatchedFiles::METHOD.to_string(),
1442+
}];
1443+
1444+
self.send_request::<request::UnregisterCapability>(
1445+
lsp_types::UnregistrationParams {
1446+
unregisterations: unregistrations,
1447+
},
1448+
|_, _| Ok(()),
1449+
);
1450+
1451+
// Register the new watcher
14321452
let registrations = vec![lsp_types::Registration {
1433-
id: "workspace/didChangeWatchedFiles".to_string(),
1453+
id: registration_id,
14341454
method: notification::DidChangeWatchedFiles::METHOD.to_string(),
14351455
register_options: Some(serde_json::to_value(register_options).unwrap()),
14361456
}];

0 commit comments

Comments
 (0)