diff options
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 90 |
1 files changed, 83 insertions, 7 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 9594e6c7e..754ccd680 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use base64::Engine; use deno_ast::MediaType; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; @@ -30,6 +31,9 @@ use std::env; use std::fmt::Write as _; use std::path::PathBuf; use std::sync::Arc; +use tokio::sync::mpsc::unbounded_channel; +use tokio::sync::mpsc::UnboundedReceiver; +use tokio::sync::mpsc::UnboundedSender; use tokio_util::sync::CancellationToken; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::jsonrpc::Result as LspResult; @@ -177,6 +181,44 @@ pub struct StateSnapshot { pub npm: Option<StateNpmSnapshot>, } +type LanguageServerTaskFn = Box<dyn FnOnce(LanguageServer) + Send + Sync>; + +/// Used to queue tasks from inside of the language server lock that must be +/// commenced from outside of it. For example, queue a request to cache a module +/// after having loaded a config file which references it. +#[derive(Debug)] +struct LanguageServerTaskQueue { + task_tx: UnboundedSender<LanguageServerTaskFn>, + /// This is moved out to its own task after initializing. + task_rx: Option<UnboundedReceiver<LanguageServerTaskFn>>, +} + +impl Default for LanguageServerTaskQueue { + fn default() -> Self { + let (task_tx, task_rx) = unbounded_channel(); + Self { + task_tx, + task_rx: Some(task_rx), + } + } +} + +impl LanguageServerTaskQueue { + fn queue_task(&self, task_fn: LanguageServerTaskFn) -> bool { + self.task_tx.send(task_fn).is_ok() + } + + /// Panics if called more than once. + fn start(&mut self, ls: LanguageServer) { + let mut task_rx = self.task_rx.take().unwrap(); + spawn(async move { + while let Some(task_fn) = task_rx.recv().await { + task_fn(ls.clone()); + } + }); + } +} + #[derive(Debug)] pub struct Inner { /// Cached versions of "fixed" assets that can either be inlined in Rust or @@ -196,6 +238,7 @@ pub struct Inner { /// on disk or "open" within the client. pub documents: Documents, http_client: Arc<HttpClient>, + task_queue: LanguageServerTaskQueue, /// Handles module registries, which allow discovery of modules module_registries: ModuleRegistry, /// The path to the module registries cache @@ -500,6 +543,7 @@ impl Inner { maybe_import_map_uri: None, maybe_package_json: None, fmt_options: Default::default(), + task_queue: Default::default(), lint_options: Default::default(), maybe_testing_server: None, module_registries, @@ -1023,6 +1067,41 @@ impl Inner { self.lint_options = lint_options; self.fmt_options = fmt_options; self.recreate_http_client_and_dependents().await?; + if let Some(config_file) = self.config.maybe_config_file() { + if let Ok((compiler_options, _)) = config_file.to_compiler_options() { + if let Some(compiler_options_obj) = compiler_options.as_object() { + if let Some(jsx_import_source) = + compiler_options_obj.get("jsxImportSource") + { + if let Some(jsx_import_source) = jsx_import_source.as_str() { + let cache_params = lsp_custom::CacheParams { + referrer: TextDocumentIdentifier { + uri: config_file.specifier.clone(), + }, + uris: vec![TextDocumentIdentifier { + uri: Url::parse(&format!( + "data:application/typescript;base64,{}", + base64::engine::general_purpose::STANDARD.encode( + format!("import '{jsx_import_source}/jsx-runtime';") + ) + )) + .unwrap(), + }], + }; + self.task_queue.queue_task(Box::new(|ls: LanguageServer| { + spawn(async move { + if let Err(err) = + ls.cache_request(Some(json!(cache_params))).await + { + lsp_warn!("{}", err); + } + }); + })); + } + } + } + } + } } Ok(()) @@ -3257,9 +3336,8 @@ impl tower_lsp::LanguageServer for LanguageServer { ls.refresh_documents_config().await; ls.diagnostics_server.invalidate_all(); ls.send_diagnostics_update(); - } - - lsp_log!("Server ready."); + ls.task_queue.start(self.clone()); + }; if upgrade_check_enabled() { // spawn to avoid lsp send/sync requirement, but also just @@ -3282,6 +3360,8 @@ impl tower_lsp::LanguageServer for LanguageServer { } }); } + + lsp_log!("Server ready."); } async fn shutdown(&self) -> LspResult<()> { @@ -3596,10 +3676,6 @@ impl Inner { let referrer = self .url_map .normalize_url(¶ms.referrer.uri, LspUrlKind::File); - if !self.is_diagnosable(&referrer) { - return Ok(None); - } - let mark = self.performance.mark_with_args("lsp.cache", ¶ms); let roots = if !params.uris.is_empty() { params |