summaryrefslogtreecommitdiff
path: root/cli/lsp/documents.rs
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-05-01 11:24:25 +0530
committerGitHub <noreply@github.com>2024-05-01 05:54:25 +0000
commit7f80d90f1c70dfc4f5bb6934e65338817ff265d2 (patch)
tree041e7226949d2f644b8cbc4f641200b9d55bec7e /cli/lsp/documents.rs
parent56bf634fa9721832b605dd91ced5329e0329a8f8 (diff)
Revert "refactor(lsp): move fields from Documents to LspResolver" (#23626)
This reverts commit 5cae3439912ad60eb2866f3d4372a5fe4d0de957. ![image](https://github.com/denoland/deno/assets/34997667/e68e40a1-7baf-453b-80d9-f8b6aae186c7) 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.rs178
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();