diff options
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 201 |
1 files changed, 190 insertions, 11 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 8e8a751a2..d1f6c67ec 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -48,6 +48,7 @@ use super::diagnostics; use super::diagnostics::DiagnosticSource; use super::documents::DocumentCache; use super::performance::Performance; +use super::registries; use super::sources; use super::sources::Sources; use super::text; @@ -58,6 +59,9 @@ use super::tsc::Assets; use super::tsc::TsServer; use super::urls; +pub const REGISTRIES_PATH: &str = "registries"; +const SOURCES_PATH: &str = "deps"; + lazy_static::lazy_static! { static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap(); static ref EXPORT_MODIFIER: Regex = Regex::new(r"\bexport\b").unwrap(); @@ -71,6 +75,7 @@ pub struct StateSnapshot { pub assets: Assets, pub config: Config, pub documents: DocumentCache, + pub module_registries: registries::ModuleRegistry, pub performance: Performance, pub sources: Sources, } @@ -87,6 +92,10 @@ pub(crate) struct Inner { diagnostics_server: diagnostics::DiagnosticsServer, /// The "in-memory" documents in the editor which can be updated and changed. documents: DocumentCache, + /// Handles module registries, which allow discovery of modules + module_registries: registries::ModuleRegistry, + /// The path to the module registries cache + module_registries_location: PathBuf, /// An optional URL which provides the location of a TypeScript configuration /// file which will be used by the Deno LSP. maybe_config_uri: Option<Url>, @@ -119,8 +128,11 @@ impl Inner { let maybe_custom_root = env::var("DENO_DIR").map(String::into).ok(); let dir = deno_dir::DenoDir::new(maybe_custom_root) .expect("could not access DENO_DIR"); - let location = dir.root.join("deps"); - let sources = Sources::new(&location); + let module_registries_location = dir.root.join(REGISTRIES_PATH); + let module_registries = + registries::ModuleRegistry::new(&module_registries_location); + let sources_location = dir.root.join(SOURCES_PATH); + let sources = Sources::new(&sources_location); let ts_server = Arc::new(TsServer::new()); let performance = Performance::default(); let diagnostics_server = diagnostics::DiagnosticsServer::new(); @@ -134,6 +146,8 @@ impl Inner { maybe_config_uri: Default::default(), maybe_import_map: Default::default(), maybe_import_map_uri: Default::default(), + module_registries, + module_registries_location, navigation_trees: Default::default(), performance, sources, @@ -276,6 +290,7 @@ impl Inner { assets: self.assets.clone(), config: self.config.clone(), documents: self.documents.clone(), + module_registries: self.module_registries.clone(), performance: self.performance.clone(), sources: self.sources.clone(), } @@ -328,6 +343,22 @@ impl Inner { Ok(()) } + async fn update_registries(&mut self) -> Result<(), AnyError> { + let mark = self.performance.mark("update_registries"); + for (registry, enabled) in self.config.settings.suggest.imports.hosts.iter() + { + if *enabled { + info!("Enabling auto complete registry for: {}", registry); + self.module_registries.enable(registry).await?; + } else { + info!("Disabling auto complete registry for: {}", registry); + self.module_registries.disable(registry).await?; + } + } + self.performance.measure(mark); + Ok(()) + } + async fn update_tsconfig(&mut self) -> Result<(), AnyError> { let mark = self.performance.mark("update_tsconfig"); let mut tsconfig = TsConfig::new(json!({ @@ -495,6 +526,13 @@ impl Inner { .show_message(MessageType::Warning, err.to_string()) .await; } + // Check to see if we need to setup any module registries + if let Err(err) = self.update_registries().await { + self + .client + .show_message(MessageType::Warning, err.to_string()) + .await; + } if self .config @@ -628,6 +666,12 @@ impl Inner { .show_message(MessageType::Warning, err.to_string()) .await; } + if let Err(err) = self.update_registries().await { + self + .client + .show_message(MessageType::Warning, err.to_string()) + .await; + } if let Err(err) = self.update_tsconfig().await { self .client @@ -1394,7 +1438,9 @@ impl Inner { &specifier, ¶ms.text_document_position.position, &self.snapshot(), - ) { + ) + .await + { Some(response) } else { let line_index = @@ -1659,16 +1705,12 @@ impl Inner { ) -> LspResult<Option<Value>> { match method { "deno/cache" => match params.map(serde_json::from_value) { - Some(Ok(params)) => Ok(Some( - serde_json::to_value(self.cache(params).await?).map_err(|err| { - error!("Failed to serialize cache response: {}", err); - LspError::internal_error() - })?, - )), + Some(Ok(params)) => self.cache(params).await, Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), None => Err(LspError::invalid_params("Missing parameters")), }, "deno/performance" => Ok(Some(self.get_performance())), + "deno/reloadImportRegistries" => self.reload_import_registries().await, "deno/virtualTextDocument" => match params.map(serde_json::from_value) { Some(Ok(params)) => Ok(Some( serde_json::to_value(self.virtual_text_document(params).await?) @@ -1979,7 +2021,7 @@ struct VirtualTextDocumentParams { impl Inner { /// Similar to `deno cache` on the command line, where modules will be cached /// in the Deno cache, including any of their dependencies. - async fn cache(&mut self, params: CacheParams) -> LspResult<bool> { + async fn cache(&mut self, params: CacheParams) -> LspResult<Option<Value>> { let mark = self.performance.mark("cache"); let referrer = self.url_map.normalize_url(¶ms.referrer.uri); if !params.uris.is_empty() { @@ -2020,7 +2062,7 @@ impl Inner { LspError::internal_error() })?; self.performance.measure(mark); - Ok(true) + Ok(Some(json!(true))) } fn get_performance(&self) -> Value { @@ -2028,6 +2070,22 @@ impl Inner { json!({ "averages": averages }) } + async fn reload_import_registries(&mut self) -> LspResult<Option<Value>> { + fs::remove_dir_all(&self.module_registries_location) + .await + .map_err(|err| { + error!("Unable to remove registries cache: {}", err); + LspError::internal_error() + })?; + self.module_registries = + registries::ModuleRegistry::new(&self.module_registries_location); + self.update_registries().await.map_err(|err| { + error!("Unable to update registries: {}", err); + LspError::internal_error() + })?; + Ok(Some(json!(true))) + } + async fn virtual_text_document( &mut self, params: VirtualTextDocumentParams, @@ -3223,6 +3281,127 @@ mod tests { harness.run().await; } + #[tokio::test] + async fn test_completions_registry() { + let _g = test_util::http_server(); + let mut harness = LspTestHarness::new(vec![ + ("initialize_request_registry.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ( + "did_open_notification_completion_registry.json", + LspResponse::None, + ), + ( + "completion_request_registry.json", + LspResponse::RequestAssert(|value| { + let response: CompletionResult = + serde_json::from_value(value).unwrap(); + let result = response.result.unwrap(); + if let CompletionResponse::List(list) = result { + assert_eq!(list.items.len(), 3); + } else { + panic!("unexpected result"); + } + }), + ), + ( + "completion_resolve_request_registry.json", + LspResponse::Request( + 4, + json!({ + "label": "v2.0.0", + "kind": 19, + "detail": "(version)", + "sortText": "0000000003", + "filterText": "http://localhost:4545/x/a@v2.0.0", + "textEdit": { + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 46 + } + }, + "newText": "http://localhost:4545/x/a@v2.0.0" + } + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + + #[tokio::test] + async fn test_completion_registry_empty_specifier() { + let _g = test_util::http_server(); + let mut harness = LspTestHarness::new(vec![ + ("initialize_request_registry.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ( + "did_open_notification_completion_registry_02.json", + LspResponse::None, + ), + ( + "completion_request_registry_02.json", + LspResponse::Request( + 2, + json!({ + "isIncomplete": false, + "items": [ + { + "label": ".", + "kind": 19, + "detail": "(local)", + "sortText": "1", + "insertText": "." + }, + { + "label": "..", + "kind": 19, + "detail": "(local)", + "sortText": "1", + "insertText": ".." + }, + { + "label": "http://localhost:4545", + "kind": 19, + "detail": "(registry)", + "sortText": "2", + "textEdit": { + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 20 + } + }, + "newText": "http://localhost:4545" + } + } + ] + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + #[derive(Deserialize)] struct PerformanceAverages { averages: Vec<PerformanceAverage>, |