use std::path::PathBuf;
use glua_code_analysis::{read_file_with_encoding, uri_to_file_path};
use lsp_types::{DidChangeWatchedFilesParams, FileChangeType, Uri};
use crate::{codestyle::should_apply_editorconfig_updates, context::ServerContextSnapshot};
pub async fn on_did_change_watched_files(
context: ServerContextSnapshot,
params: DidChangeWatchedFilesParams,
) -> Option<()> {
let (encoding, interval, apply_editorconfig_updates) = {
let analysis = context.analysis().read().await;
let emmyrc = analysis.get_emmyrc();
(
emmyrc.workspace.encoding.clone(),
emmyrc.diagnostics.diagnostic_interval.unwrap_or(500),
should_apply_editorconfig_updates(&emmyrc),
)
};
let lsp_features = context.lsp_features();
let mut watched_lua_files: Vec<(Uri, Option<String>)> = Vec::new();
let mut deleted_lua_uris: Vec<Uri> = Vec::new();
let mut editorconfig_paths: Vec<PathBuf> = Vec::new();
let mut emmyrc_dirs: Vec<PathBuf> = Vec::new();
{
let workspace = context.workspace_manager().read().await;
for file_event in params.changes.into_iter() {
let file_type = get_file_type(&file_event.uri);
match file_type {
Some(WatchedFileType::Lua) => {
if file_event.typ == FileChangeType::DELETED {
if workspace.is_workspace_file(&file_event.uri) {
deleted_lua_uris.push(file_event.uri);
}
continue;
}
if !workspace.current_open_files.contains(&file_event.uri)
&& workspace.is_workspace_file(&file_event.uri)
{
collect_lua_files(
&mut watched_lua_files,
file_event.uri,
file_event.typ,
&encoding,
);
}
}
Some(WatchedFileType::Editorconfig) => {
if file_event.typ != FileChangeType::DELETED {
if let Some(path) = uri_to_file_path(&file_event.uri) {
editorconfig_paths.push(path);
}
}
}
Some(WatchedFileType::Emmyrc) => {
if let Some(path) = uri_to_file_path(&file_event.uri) {
if let Some(dir) = path.parent() {
emmyrc_dirs.push(dir.to_path_buf());
}
}
}
None => {}
}
}
}
let file_ids = {
let mut analysis = context.analysis().write().await;
for uri in &deleted_lua_uris {
analysis.remove_file_by_uri(uri);
}
let file_ids = analysis.update_files_by_uri(watched_lua_files);
if !file_ids.is_empty() || !deleted_lua_uris.is_empty() {
context
.file_diagnostic()
.invalidate_shared_diagnostic_data();
}
file_ids
};
if !lsp_features.supports_pull_diagnostic() {
for uri in &deleted_lua_uris {
context
.file_diagnostic()
.clear_push_file_diagnostics(uri.clone())
.await;
}
}
context
.file_diagnostic()
.add_files_diagnostic_task(file_ids, interval, Some(context.debounced_analysis_arc()))
.await;
{
let workspace = context.workspace_manager().read().await;
if apply_editorconfig_updates {
for path in &editorconfig_paths {
workspace.update_editorconfig(path.clone());
}
} else if !editorconfig_paths.is_empty() {
log::info!(
"skipping .editorconfig watched-file update because format.configPrecedence=preferGluarc"
);
}
for dir in emmyrc_dirs {
workspace
.add_update_emmyrc_task(dir, context.workspace_manager_arc())
.await;
}
}
Some(())
}
fn collect_lua_files(
watched_lua_files: &mut Vec<(Uri, Option<String>)>,
uri: Uri,
file_change_event: FileChangeType,
encoding: &str,
) {
match file_change_event {
FileChangeType::CREATED | FileChangeType::CHANGED => {
let Some(path) = uri_to_file_path(&uri) else {
return;
};
if let Some(text) = read_file_with_encoding(&path, encoding) {
watched_lua_files.push((uri, Some(text)));
}
}
FileChangeType::DELETED => {
watched_lua_files.push((uri, None));
}
_ => {}
}
}
enum WatchedFileType {
Lua,
Editorconfig,
Emmyrc,
}
fn get_file_type(uri: &Uri) -> Option<WatchedFileType> {
let path = uri_to_file_path(uri)?;
let file_name = path.file_name()?.to_str()?;
match file_name {
".editorconfig" => Some(WatchedFileType::Editorconfig),
".emmyrc.json" | ".luarc.json" | ".emmyrc.lua" | ".gluarc.json" => {
Some(WatchedFileType::Emmyrc)
}
_ => Some(WatchedFileType::Lua),
}
}