diff --git a/crates/oxc_language_server/src/backend.rs b/crates/oxc_language_server/src/backend.rs index 0bedcf1e89efe..4821be2ef5722 100644 --- a/crates/oxc_language_server/src/backend.rs +++ b/crates/oxc_language_server/src/backend.rs @@ -21,7 +21,7 @@ use tower_lsp_server::{ use tracing::{debug, error, info, warn}; use crate::{ - ConcurrentHashMap, ToolBuilder, + ConcurrentHashMap, LanguageId, ToolBuilder, capabilities::{Capabilities, DiagnosticMode, server_capabilities}, file_system::LSPFileSystem, options::WorkspaceOption, @@ -242,7 +242,8 @@ impl LanguageServer for Backend { if responsible_worker.is_none_or(|w| !std::ptr::eq(w, worker)) { continue; } - let content = self.file_system.read().await.get(uri); + let content = + self.file_system.read().await.get(uri).map(|(_, content)| content); let diagnostics = worker.run_diagnostic(uri, content.as_deref()).await; match diagnostics { Err(err) => { @@ -659,7 +660,11 @@ impl LanguageServer for Backend { let content = params.text_document.text; - self.file_system.write().await.set(uri.clone(), content.clone()); + self.file_system.write().await.set_with_language( + uri.clone(), + LanguageId::new(params.text_document.language_id), + content.clone(), + ); if self.capabilities.get().is_some_and(|cap| cap.diagnostic_mode == DiagnosticMode::Push) { match worker.run_diagnostic(&uri, Some(&content)).await { @@ -757,8 +762,9 @@ impl LanguageServer for Backend { RelatedFullDocumentDiagnosticReport::default(), ))); }; - let diagnostics = - worker.run_diagnostic(uri, self.file_system.read().await.get(uri).as_deref()).await; + + let content = self.file_system.read().await.get(uri).map(|(_, content)| content); + let diagnostics = worker.run_diagnostic(uri, content.as_deref()).await; let diagnostics = match diagnostics { Err(err) => { @@ -820,7 +826,8 @@ impl LanguageServer for Backend { let Some(worker) = Self::find_worker_for_uri(&workers, uri) else { return Ok(None); }; - match worker.format_file(uri, self.file_system.read().await.get(uri).as_deref()).await { + let content = self.file_system.read().await.get(uri).map(|(_, content)| content); + match worker.format_file(uri, content.as_deref()).await { Ok(edits) => { if edits.is_empty() { return Ok(None); diff --git a/crates/oxc_language_server/src/file_system.rs b/crates/oxc_language_server/src/file_system.rs index 4fdfc100ddca5..7efd9cf3adeff 100644 --- a/crates/oxc_language_server/src/file_system.rs +++ b/crates/oxc_language_server/src/file_system.rs @@ -1,10 +1,10 @@ use tower_lsp_server::ls_types::Uri; -use crate::ConcurrentHashMap; +use crate::{ConcurrentHashMap, LanguageId}; #[derive(Debug, Default)] pub struct LSPFileSystem { - files: ConcurrentHashMap, + files: ConcurrentHashMap, } impl LSPFileSystem { @@ -13,10 +13,19 @@ impl LSPFileSystem { } pub fn set(&self, uri: Uri, content: String) { - self.files.pin().insert(uri, content); + let language_id = self.get_language_id(&uri).unwrap_or_default(); + self.files.pin().insert(uri, (language_id, content)); } - pub fn get(&self, uri: &Uri) -> Option { + pub fn set_with_language(&self, uri: Uri, language_id: LanguageId, content: String) { + self.files.pin().insert(uri, (language_id, content)); + } + + pub fn get_language_id(&self, uri: &Uri) -> Option { + self.files.pin().get(uri).map(|(lang, _)| lang.clone()) + } + + pub fn get(&self, uri: &Uri) -> Option<(LanguageId, String)> { self.files.pin().get(uri).cloned() } diff --git a/crates/oxc_language_server/src/language_id.rs b/crates/oxc_language_server/src/language_id.rs new file mode 100644 index 0000000000000..24b2961183a2a --- /dev/null +++ b/crates/oxc_language_server/src/language_id.rs @@ -0,0 +1,17 @@ +/// Represents language IDs passed from the client in `textDocument/didOpen` notifications. +/// +/// These are used to select the appropriate parser strategy for a given file. +/// It is the tool's responsibility to use the correct parser strategy for a file +/// based on its file extension, but newly created files may not have an extension, +/// so we rely on the language ID to determine which parser strategy to use. +/// +/// For a more complete list of known language identifiers, see: +/// +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct LanguageId(String); + +impl LanguageId { + pub fn new(id: String) -> Self { + Self(id) + } +} diff --git a/crates/oxc_language_server/src/lib.rs b/crates/oxc_language_server/src/lib.rs index c2c115f0f2347..b08b8b9496e4a 100644 --- a/crates/oxc_language_server/src/lib.rs +++ b/crates/oxc_language_server/src/lib.rs @@ -4,6 +4,7 @@ use tower_lsp_server::{LspService, Server, ls_types::ServerInfo}; mod backend; mod capabilities; mod file_system; +mod language_id; mod options; #[cfg(test)] mod tests; @@ -11,6 +12,7 @@ mod tool; mod worker; pub use crate::capabilities::{Capabilities, DiagnosticMode}; +pub use crate::language_id::LanguageId; pub use crate::tool::{DiagnosticResult, Tool, ToolBuilder, ToolRestartChanges}; pub type ConcurrentHashMap = papaya::HashMap; diff --git a/crates/oxc_language_server/src/worker.rs b/crates/oxc_language_server/src/worker.rs index 21d1a276cb7b7..d3c66c0179ec5 100644 --- a/crates/oxc_language_server/src/worker.rs +++ b/crates/oxc_language_server/src/worker.rs @@ -373,9 +373,8 @@ impl WorkspaceWorker { }; for uri in file_system.keys() { - let Ok(mut reports) = - tool.run_diagnostic(&uri, file_system.get(&uri).as_deref()) - else { + let content = file_system.get(&uri).map(|(_, content)| content); + let Ok(mut reports) = tool.run_diagnostic(&uri, content.as_deref()) else { // If diagnostics could not be run, skip this URI, but continue with others // TODO: Should we aggregate errors instead? One by one, or all together? continue;