diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2024-05-23 17:31:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-23 17:31:56 +0100 |
commit | 0a30897925fd8940884231b97474f4ea76e5ed28 (patch) | |
tree | ca0081c4eb455e6ac1ebe71b24853b2262f4c63a /cli/lsp/documents.rs | |
parent | 143ea4759fa32bcd32ff983caeaec08929a52e80 (diff) |
refactor(lsp): determine file referrer for each document (#23867)
Diffstat (limited to 'cli/lsp/documents.rs')
-rw-r--r-- | cli/lsp/documents.rs | 170 |
1 files changed, 138 insertions, 32 deletions
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index df15bff2a..ab053336f 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -266,6 +266,9 @@ pub struct Document { /// Contains the last-known-good set of dependencies from parsing the module. config: Arc<Config>, dependencies: Arc<IndexMap<String, deno_graph::Dependency>>, + // TODO(nayeemrmn): This is unused, use it for scope attribution for remote + // modules. + file_referrer: Option<ModuleSpecifier>, maybe_types_dependency: Option<Arc<deno_graph::TypesDependency>>, maybe_fs_version: Option<String>, line_index: Arc<LineIndex>, @@ -295,6 +298,7 @@ impl Document { resolver: Arc<LspResolver>, config: Arc<Config>, cache: &Arc<LspCache>, + file_referrer: Option<ModuleSpecifier>, ) -> Arc<Self> { let text_info = SourceTextInfo::new(content); let media_type = resolve_media_type( @@ -310,6 +314,7 @@ impl Document { text_info.clone(), maybe_headers.as_ref(), media_type, + file_referrer.as_ref(), &resolver, ) } else { @@ -329,6 +334,7 @@ impl Document { Arc::new(Self { config, dependencies, + file_referrer: file_referrer.filter(|_| specifier.scheme() != "file"), maybe_types_dependency, maybe_fs_version: calculate_fs_version(cache, &specifier), line_index, @@ -370,6 +376,7 @@ impl Document { &self.specifier, &parsed_source_result, self.maybe_headers.as_ref(), + self.file_referrer.as_ref(), &resolver, ) .ok(); @@ -384,8 +391,10 @@ impl Document { maybe_test_module_fut = get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config); } else { - let graph_resolver = resolver.as_graph_resolver(); - let npm_resolver = resolver.as_graph_npm_resolver(); + let graph_resolver = + resolver.as_graph_resolver(self.file_referrer.as_ref()); + let npm_resolver = + resolver.as_graph_npm_resolver(self.file_referrer.as_ref()); dependencies = Arc::new( self .dependencies @@ -420,6 +429,7 @@ impl Document { config, // updated properties dependencies, + file_referrer: self.file_referrer.clone(), maybe_types_dependency, maybe_navigation_tree: Mutex::new(None), // maintain - this should all be copies/clones @@ -475,6 +485,7 @@ impl Document { text_info.clone(), self.maybe_headers.as_ref(), media_type, + self.file_referrer.as_ref(), self.resolver.as_ref(), ) } else { @@ -499,6 +510,7 @@ impl Document { Ok(Arc::new(Self { config: self.config.clone(), specifier: self.specifier.clone(), + file_referrer: self.file_referrer.clone(), maybe_fs_version: self.maybe_fs_version.clone(), maybe_language_id: self.maybe_language_id, dependencies, @@ -522,6 +534,7 @@ impl Document { Arc::new(Self { config: self.config.clone(), specifier: self.specifier.clone(), + file_referrer: self.file_referrer.clone(), maybe_fs_version: calculate_fs_version(cache, &self.specifier), maybe_language_id: self.maybe_language_id, dependencies: self.dependencies.clone(), @@ -543,6 +556,7 @@ impl Document { Arc::new(Self { config: self.config.clone(), specifier: self.specifier.clone(), + file_referrer: self.file_referrer.clone(), maybe_fs_version: calculate_fs_version(cache, &self.specifier), maybe_language_id: self.maybe_language_id, dependencies: self.dependencies.clone(), @@ -564,6 +578,10 @@ impl Document { &self.specifier } + pub fn file_referrer(&self) -> Option<&ModuleSpecifier> { + self.file_referrer.as_ref() + } + pub fn content(&self) -> Arc<str> { self.text_info.text() } @@ -729,6 +747,7 @@ impl FileSystemDocuments { resolver: &Arc<LspResolver>, config: &Arc<Config>, cache: &Arc<LspCache>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<Arc<Document>> { let new_fs_version = calculate_fs_version(cache, specifier); let old_doc = self.docs.get(specifier).map(|v| v.value().clone()); @@ -745,7 +764,7 @@ impl FileSystemDocuments { }; if dirty { // attempt to update the file on the file system - self.refresh_document(specifier, resolver, config, cache) + self.refresh_document(specifier, resolver, config, cache, file_referrer) } else { old_doc } @@ -759,6 +778,7 @@ impl FileSystemDocuments { resolver: &Arc<LspResolver>, config: &Arc<Config>, cache: &Arc<LspCache>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<Arc<Document>> { let doc = if specifier.scheme() == "file" { let path = specifier_to_file_path(specifier).ok()?; @@ -774,6 +794,7 @@ impl FileSystemDocuments { resolver.clone(), config.clone(), cache, + file_referrer.cloned(), ) } else if specifier.scheme() == "data" { let source = deno_graph::source::RawDataUrl::parse(specifier) @@ -789,6 +810,7 @@ impl FileSystemDocuments { resolver.clone(), config.clone(), cache, + file_referrer.cloned(), ) } else { let http_cache = cache.root_vendor_or_global(); @@ -818,6 +840,7 @@ impl FileSystemDocuments { resolver.clone(), config.clone(), cache, + file_referrer.cloned(), ) }; self.docs.insert(specifier.clone(), doc.clone()); @@ -882,6 +905,7 @@ impl Documents { version: i32, language_id: LanguageId, content: Arc<str>, + file_referrer: Option<ModuleSpecifier>, ) -> Arc<Document> { let document = Document::new( specifier.clone(), @@ -895,6 +919,7 @@ impl Documents { self.resolver.clone(), self.config.clone(), &self.cache, + file_referrer, ); self.file_system_docs.remove_document(&specifier); @@ -964,6 +989,19 @@ impl Documents { self.file_system_docs.set_dirty(true); } + pub fn get_file_referrer<'a>( + &self, + specifier: &'a ModuleSpecifier, + ) -> Option<Cow<'a, ModuleSpecifier>> { + if specifier.scheme() == "file" { + Some(Cow::Borrowed(specifier)) + } else { + self + .get(specifier) + .and_then(|d| d.file_referrer().cloned().map(Cow::Owned)) + } + } + /// Return `true` if the provided specifier can be resolved to a document, /// otherwise `false`. pub fn contains_import( @@ -971,9 +1009,10 @@ impl Documents { specifier: &str, referrer: &ModuleSpecifier, ) -> bool { + let file_referrer = self.get_file_referrer(referrer); let maybe_specifier = self .resolver - .as_graph_resolver() + .as_graph_resolver(file_referrer.as_deref()) .resolve( specifier, &deno_graph::Range { @@ -985,7 +1024,7 @@ impl Documents { ) .ok(); if let Some(import_specifier) = maybe_specifier { - self.exists(&import_specifier) + self.exists(&import_specifier, file_referrer.as_deref()) } else { false } @@ -994,23 +1033,32 @@ impl Documents { pub fn resolve_document_specifier( &self, specifier: &ModuleSpecifier, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<ModuleSpecifier> { 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 + .resolver + .jsr_to_registry_url(&jsr_req_ref, file_referrer)?, + ) } else { Cow::Borrowed(specifier) }; if !DOCUMENT_SCHEMES.contains(&specifier.scheme()) { return None; } - self.resolver.resolve_redirects(&specifier) + self.resolver.resolve_redirects(&specifier, file_referrer) } /// Return `true` if the specifier can be resolved to a document. - pub fn exists(&self, specifier: &ModuleSpecifier) -> bool { - let specifier = self.resolve_document_specifier(specifier); + pub fn exists( + &self, + specifier: &ModuleSpecifier, + file_referrer: Option<&ModuleSpecifier>, + ) -> bool { + let specifier = self.resolve_document_specifier(specifier, file_referrer); if let Some(specifier) = specifier { if self.open_docs.contains_key(&specifier) { return true; @@ -1043,11 +1091,38 @@ impl Documents { } /// Return a document for the specifier. - pub fn get( + pub fn get(&self, specifier: &ModuleSpecifier) -> Option<Arc<Document>> { + if let Some(document) = self.open_docs.get(specifier) { + Some(document.clone()) + } else { + let old_doc = self + .file_system_docs + .docs + .get(specifier) + .map(|d| d.value().clone()); + if let Some(old_doc) = old_doc { + self.file_system_docs.get( + specifier, + &self.resolver, + &self.config, + &self.cache, + old_doc.file_referrer(), + ) + } else { + None + } + } + } + + /// Return a document for the specifier. + pub fn get_or_load( &self, - original_specifier: &ModuleSpecifier, + specifier: &ModuleSpecifier, + referrer: &ModuleSpecifier, ) -> Option<Arc<Document>> { - let specifier = self.resolve_document_specifier(original_specifier)?; + let file_referrer = self.get_file_referrer(referrer); + let specifier = + self.resolve_document_specifier(specifier, file_referrer.as_deref())?; if let Some(document) = self.open_docs.get(&specifier) { Some(document.clone()) } else { @@ -1056,6 +1131,7 @@ impl Documents { &self.resolver, &self.config, &self.cache, + file_referrer.as_deref(), ) } } @@ -1110,6 +1186,7 @@ impl Documents { referrer: &ModuleSpecifier, ) -> Vec<Option<(ModuleSpecifier, MediaType)>> { let document = self.get(referrer); + let file_referrer = document.as_ref().and_then(|d| d.file_referrer()); let dependencies = document.as_ref().map(|d| d.dependencies()); let mut results = Vec::new(); for specifier in specifiers { @@ -1124,22 +1201,36 @@ impl Documents { dependencies.as_ref().and_then(|d| d.get(specifier)) { if let Some(specifier) = dep.maybe_type.maybe_specifier() { - results.push(self.resolve_dependency(specifier, referrer)); + results.push(self.resolve_dependency( + specifier, + referrer, + file_referrer, + )); } else if let Some(specifier) = dep.maybe_code.maybe_specifier() { - results.push(self.resolve_dependency(specifier, referrer)); + results.push(self.resolve_dependency( + specifier, + referrer, + file_referrer, + )); } else { results.push(None); } - } else if let Ok(specifier) = self.resolver.as_graph_resolver().resolve( - specifier, - &deno_graph::Range { - specifier: referrer.clone(), - start: deno_graph::Position::zeroed(), - end: deno_graph::Position::zeroed(), - }, - ResolutionMode::Types, - ) { - results.push(self.resolve_dependency(&specifier, referrer)); + } else if let Ok(specifier) = + self.resolver.as_graph_resolver(file_referrer).resolve( + specifier, + &deno_graph::Range { + specifier: referrer.clone(), + start: deno_graph::Position::zeroed(), + end: deno_graph::Position::zeroed(), + }, + ResolutionMode::Types, + ) + { + results.push(self.resolve_dependency( + &specifier, + referrer, + file_referrer, + )); } else { results.push(None); } @@ -1203,6 +1294,7 @@ impl Documents { &self.resolver, &self.config, &self.cache, + None, ); } } @@ -1283,6 +1375,7 @@ impl Documents { &self, specifier: &ModuleSpecifier, referrer: &ModuleSpecifier, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<(ModuleSpecifier, MediaType)> { if let Some(module_name) = specifier.as_str().strip_prefix("node:") { if deno_node::is_builtin_node_module(module_name) { @@ -1294,13 +1387,15 @@ impl Documents { } if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(specifier) { - return self.resolver.npm_to_file_url(&npm_ref, referrer); + return self + .resolver + .npm_to_file_url(&npm_ref, referrer, file_referrer); } - let Some(doc) = self.get(specifier) else { + let Some(doc) = self.get_or_load(specifier, referrer) else { return Some((specifier.clone(), MediaType::from_specifier(specifier))); }; - if let Some(specifier) = doc.maybe_types_dependency().maybe_specifier() { - self.resolve_dependency(specifier, referrer) + if let Some(types) = doc.maybe_types_dependency().maybe_specifier() { + self.resolve_dependency(types, specifier, file_referrer) } else { let media_type = doc.media_type(); Some((doc.specifier().clone(), media_type)) @@ -1364,11 +1459,17 @@ fn parse_and_analyze_module( text_info: SourceTextInfo, maybe_headers: Option<&HashMap<String, String>>, media_type: MediaType, + file_referrer: Option<&ModuleSpecifier>, resolver: &LspResolver, ) -> (Option<ParsedSourceResult>, Option<ModuleResult>) { let parsed_source_result = parse_source(specifier, text_info, media_type); - let module_result = - analyze_module(specifier, &parsed_source_result, maybe_headers, resolver); + let module_result = analyze_module( + specifier, + &parsed_source_result, + maybe_headers, + file_referrer, + resolver, + ); (Some(parsed_source_result), Some(module_result)) } @@ -1391,6 +1492,7 @@ fn analyze_module( specifier: &ModuleSpecifier, parsed_source_result: &ParsedSourceResult, maybe_headers: Option<&HashMap<String, String>>, + file_referrer: Option<&ModuleSpecifier>, resolver: &LspResolver, ) -> ModuleResult { match parsed_source_result { @@ -1404,8 +1506,8 @@ fn analyze_module( // dynamic imports like import(`./dir/${something}`) in the LSP file_system: &deno_graph::source::NullFileSystem, jsr_url_provider: &CliJsrUrlProvider, - maybe_resolver: Some(resolver.as_graph_resolver()), - maybe_npm_resolver: Some(resolver.as_graph_npm_resolver()), + maybe_resolver: Some(resolver.as_graph_resolver(file_referrer)), + maybe_npm_resolver: Some(resolver.as_graph_npm_resolver(file_referrer)), }, )), Err(err) => Err(deno_graph::ModuleGraphError::ModuleError( @@ -1448,6 +1550,7 @@ console.log(b); 1, "javascript".parse().unwrap(), content.into(), + None, ); assert!(document.is_diagnosable()); assert!(document.is_open()); @@ -1473,6 +1576,7 @@ console.log(b); 1, "javascript".parse().unwrap(), content.into(), + None, ); documents .change( @@ -1517,6 +1621,7 @@ console.log(b, "hello deno"); 1, LanguageId::TypeScript, "".into(), + None, ); // make a clone of the document store and close the document in that one @@ -1586,6 +1691,7 @@ console.log(b, "hello deno"); 1, LanguageId::TypeScript, "import {} from 'test';".into(), + None, ); assert_eq!( |