diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2024-05-01 11:24:25 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-01 05:54:25 +0000 |
commit | 7f80d90f1c70dfc4f5bb6934e65338817ff265d2 (patch) | |
tree | 041e7226949d2f644b8cbc4f641200b9d55bec7e /cli/lsp/documents.rs | |
parent | 56bf634fa9721832b605dd91ced5329e0329a8f8 (diff) |
Revert "refactor(lsp): move fields from Documents to LspResolver" (#23626)
This reverts commit 5cae3439912ad60eb2866f3d4372a5fe4d0de957.

Caused a +3.3s regression in one of the LSP benchmarks at
deno.land/benchmarks.
Diffstat (limited to 'cli/lsp/documents.rs')
-rw-r--r-- | cli/lsp/documents.rs | 178 |
1 files changed, 153 insertions, 25 deletions
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 60b2385cb..71cc63f83 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -12,6 +12,7 @@ use super::tsc::AssetDocument; use crate::cache::HttpCache; use crate::graph_util::CliJsrUrlProvider; +use crate::jsr::JsrCacheResolver; use crate::lsp::logging::lsp_warn; use crate::resolver::SloppyImportsFsEntry; use crate::resolver::SloppyImportsResolution; @@ -31,6 +32,7 @@ use deno_core::futures::FutureExt; use deno_core::parking_lot::Mutex; use deno_core::ModuleSpecifier; use deno_graph::source::ResolutionMode; +use deno_graph::GraphImport; use deno_graph::Resolution; use deno_lockfile::Lockfile; use deno_runtime::deno_node; @@ -714,6 +716,64 @@ pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range { } } +#[derive(Debug)] +struct RedirectResolver { + cache: Arc<dyn HttpCache>, + redirects: Mutex<HashMap<ModuleSpecifier, ModuleSpecifier>>, +} + +impl RedirectResolver { + pub fn new(cache: Arc<dyn HttpCache>) -> Self { + Self { + cache, + redirects: Mutex::new(HashMap::new()), + } + } + + pub fn resolve( + &self, + specifier: &ModuleSpecifier, + ) -> Option<ModuleSpecifier> { + let scheme = specifier.scheme(); + if !DOCUMENT_SCHEMES.contains(&scheme) { + return None; + } + + if scheme == "http" || scheme == "https" { + let mut redirects = self.redirects.lock(); + if let Some(specifier) = redirects.get(specifier) { + Some(specifier.clone()) + } else { + let redirect = self.resolve_remote(specifier, 10)?; + redirects.insert(specifier.clone(), redirect.clone()); + Some(redirect) + } + } else { + Some(specifier.clone()) + } + } + + fn resolve_remote( + &self, + specifier: &ModuleSpecifier, + redirect_limit: usize, + ) -> Option<ModuleSpecifier> { + if redirect_limit > 0 { + let cache_key = self.cache.cache_item_key(specifier).ok()?; + let headers = self.cache.read_headers(&cache_key).ok().flatten()?; + if let Some(location) = headers.get("location") { + let redirect = + deno_core::resolve_import(location, specifier.as_str()).ok()?; + self.resolve_remote(&redirect, redirect_limit - 1) + } else { + Some(specifier.clone()) + } + } else { + None + } + } +} + #[derive(Debug, Default)] struct FileSystemDocuments { docs: DashMap<ModuleSpecifier, Arc<Document>>, @@ -858,15 +918,21 @@ pub struct Documents { open_docs: HashMap<ModuleSpecifier, Arc<Document>>, /// Documents stored on the file system. file_system_docs: Arc<FileSystemDocuments>, + /// Any imports to the context supplied by configuration files. This is like + /// the imports into the a module graph in CLI. + imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, /// A resolver that takes into account currently loaded import map and JSX /// settings. resolver: Arc<LspResolver>, + jsr_resolver: Arc<JsrCacheResolver>, lockfile: Option<Arc<Mutex<Lockfile>>>, /// The npm package requirements found in npm specifiers. npm_specifier_reqs: Arc<Vec<PackageReq>>, /// Gets if any document had a node: specifier such that a @types/node package /// should be injected. has_injected_types_node_package: bool, + /// Resolves a specifier to its final redirected to specifier. + redirect_resolver: Arc<RedirectResolver>, /// If --unstable-sloppy-imports is enabled. unstable_sloppy_imports: bool, } @@ -879,14 +945,29 @@ impl Documents { dirty: true, open_docs: HashMap::default(), file_system_docs: Default::default(), + imports: Default::default(), resolver: Default::default(), + jsr_resolver: Arc::new(JsrCacheResolver::new(cache.clone(), None)), lockfile: None, npm_specifier_reqs: Default::default(), has_injected_types_node_package: false, + redirect_resolver: Arc::new(RedirectResolver::new(cache)), unstable_sloppy_imports: false, } } + pub fn initialize(&mut self, config: &Config) { + self.config = Arc::new(config.clone()); + } + + pub fn module_graph_imports(&self) -> impl Iterator<Item = &ModuleSpecifier> { + self + .imports + .values() + .flat_map(|i| i.dependencies.values()) + .flat_map(|value| value.get_type().or_else(|| value.get_code())) + } + /// "Open" a document from the perspective of the editor, meaning that /// requests for information from the document will come from the in-memory /// representation received from the language server client, versus reading @@ -1021,14 +1102,11 @@ impl Documents { let specifier = if let Ok(jsr_req_ref) = JsrPackageReqReference::from_specifier(specifier) { - Cow::Owned(self.resolver.jsr_to_registry_url(&jsr_req_ref)?) + Cow::Owned(self.jsr_resolver.jsr_to_registry_url(&jsr_req_ref)?) } else { Cow::Borrowed(specifier) }; - if !DOCUMENT_SCHEMES.contains(&specifier.scheme()) { - return None; - } - self.resolver.resolve_redirects(&specifier) + self.redirect_resolver.resolve(&specifier) } } @@ -1201,8 +1279,7 @@ impl Documents { results.push(None); } } else if let Some(specifier) = self - .resolver - .resolve_graph_import(specifier) + .resolve_imports_dependency(specifier) .and_then(|r| r.maybe_specifier()) { results.push(self.resolve_dependency(specifier, referrer)); @@ -1231,19 +1308,62 @@ impl Documents { results } + /// Update the location of the on disk cache for the document store. + pub fn set_cache(&mut self, cache: Arc<dyn HttpCache>) { + // TODO update resolved dependencies? + self.cache = cache.clone(); + self.redirect_resolver = Arc::new(RedirectResolver::new(cache)); + self.dirty = true; + } + + pub fn get_jsr_resolver(&self) -> &Arc<JsrCacheResolver> { + &self.jsr_resolver + } + + pub fn refresh_lockfile(&mut self, lockfile: Option<Arc<Mutex<Lockfile>>>) { + self.jsr_resolver = + Arc::new(JsrCacheResolver::new(self.cache.clone(), lockfile.clone())); + self.lockfile = lockfile; + } + pub fn update_config( &mut self, config: &Config, resolver: &Arc<LspResolver>, - cache: Arc<dyn HttpCache>, workspace_files: &BTreeSet<ModuleSpecifier>, ) { self.config = Arc::new(config.clone()); - self.cache = cache; let config_data = config.tree.root_data(); let config_file = config_data.and_then(|d| d.config_file.as_deref()); self.resolver = resolver.clone(); + self.jsr_resolver = Arc::new(JsrCacheResolver::new( + self.cache.clone(), + config.tree.root_lockfile().cloned(), + )); self.lockfile = config.tree.root_lockfile().cloned(); + self.redirect_resolver = + Arc::new(RedirectResolver::new(self.cache.clone())); + let graph_resolver = self.resolver.as_graph_resolver(); + let npm_resolver = self.resolver.as_graph_npm_resolver(); + self.imports = Arc::new( + if let Some(Ok(imports)) = config_file.map(|cf| cf.to_maybe_imports()) { + imports + .into_iter() + .map(|(referrer, imports)| { + let graph_import = GraphImport::new( + &referrer, + imports, + &CliJsrUrlProvider, + Some(graph_resolver), + Some(npm_resolver), + ); + (referrer, graph_import) + }) + .collect() + } else { + IndexMap::new() + }, + ); self.unstable_sloppy_imports = config_file .map(|c| c.has_unstable("sloppy-imports")) .unwrap_or(false); @@ -1396,6 +1516,19 @@ impl Documents { Some((doc.specifier().clone(), media_type)) } } + + /// Iterate through any "imported" modules, checking to see if a dependency + /// is available. This is used to provide "global" imports like the JSX import + /// source. + fn resolve_imports_dependency(&self, specifier: &str) -> Option<&Resolution> { + for graph_imports in self.imports.values() { + let maybe_dep = graph_imports.dependencies.get(specifier); + if maybe_dep.is_some() { + return maybe_dep.map(|d| &d.maybe_type); + } + } + None + } } fn node_resolve_npm_req_ref( @@ -1569,20 +1702,20 @@ mod tests { use test_util::PathRef; use test_util::TempDir; - fn setup(temp_dir: &TempDir) -> (Documents, PathRef, Arc<dyn HttpCache>) { + fn setup(temp_dir: &TempDir) -> (Documents, PathRef) { let location = temp_dir.path().join("deps"); let cache = Arc::new(GlobalHttpCache::new( location.to_path_buf(), RealDenoCacheEnv, )); - let documents = Documents::new(cache.clone()); - (documents, location, cache) + let documents = Documents::new(cache); + (documents, location) } #[test] fn test_documents_open_close() { let temp_dir = TempDir::new(); - let (mut documents, _, _) = setup(&temp_dir); + let (mut documents, _) = setup(&temp_dir); let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap(); let content = r#"import * as b from "./b.ts"; console.log(b); @@ -1608,7 +1741,7 @@ console.log(b); #[test] fn test_documents_change() { let temp_dir = TempDir::new(); - let (mut documents, _, _) = setup(&temp_dir); + let (mut documents, _) = setup(&temp_dir); let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap(); let content = r#"import * as b from "./b.ts"; console.log(b); @@ -1652,7 +1785,7 @@ console.log(b, "hello deno"); // it should never happen that a user of this API causes this to happen, // but we'll guard against it anyway let temp_dir = TempDir::new(); - let (mut documents, documents_path, _) = setup(&temp_dir); + let (mut documents, documents_path) = setup(&temp_dir); let file_path = documents_path.join("file.ts"); let file_specifier = ModuleSpecifier::from_file_path(&file_path).unwrap(); documents_path.create_dir_all(); @@ -1680,7 +1813,7 @@ console.log(b, "hello deno"); // it should never happen that a user of this API causes this to happen, // but we'll guard against it anyway let temp_dir = TempDir::new(); - let (mut documents, documents_path, cache) = setup(&temp_dir); + let (mut documents, documents_path) = setup(&temp_dir); fs::create_dir_all(&documents_path).unwrap(); let file1_path = documents_path.join("file1.ts"); @@ -1729,14 +1862,9 @@ console.log(b, "hello deno"); .await; let resolver = LspResolver::default() - .with_new_config(&config, cache.clone(), None, None) + .with_new_config(&config, None, None) .await; - documents.update_config( - &config, - &resolver, - cache.clone(), - &workspace_files, - ); + documents.update_config(&config, &resolver, &workspace_files); // open the document let document = documents.open( @@ -1778,9 +1906,9 @@ console.log(b, "hello deno"); .await; let resolver = LspResolver::default() - .with_new_config(&config, cache.clone(), None, None) + .with_new_config(&config, None, None) .await; - documents.update_config(&config, &resolver, cache, &workspace_files); + documents.update_config(&config, &resolver, &workspace_files); // check the document's dependencies let document = documents.get(&file1_specifier).unwrap(); |