Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(lsp): move fields from Documents to LspResolver #23585

Merged
merged 9 commits into from Apr 30, 2024
5 changes: 2 additions & 3 deletions cli/lsp/analysis.rs
Expand Up @@ -259,8 +259,7 @@ impl<'a> TsResponseImportMapper<'a> {
let version = Version::parse_standard(segments.next()?).ok()?;
let nv = PackageNv { name, version };
let path = segments.collect::<Vec<_>>().join("/");
let jsr_resolver = self.documents.get_jsr_resolver();
let export = jsr_resolver.lookup_export_for_path(&nv, &path)?;
let export = self.resolver.jsr_lookup_export_for_path(&nv, &path)?;
let sub_path = (export != ".").then_some(export);
let mut req = None;
req = req.or_else(|| {
Expand All @@ -282,7 +281,7 @@ impl<'a> TsResponseImportMapper<'a> {
}
None
});
req = req.or_else(|| jsr_resolver.lookup_req_for_nv(&nv));
req = req.or_else(|| self.resolver.jsr_lookup_req_for_nv(&nv));
let spec_str = if let Some(req) = req {
let req_ref = PackageReqReference { req, sub_path };
JsrPackageReqReference::new(req_ref).to_string()
Expand Down
4 changes: 2 additions & 2 deletions cli/lsp/diagnostics.rs
Expand Up @@ -1591,7 +1591,7 @@ mod tests {
location.to_path_buf(),
RealDenoCacheEnv,
));
let mut documents = Documents::new(cache);
let mut documents = Documents::new(cache.clone());
for (specifier, source, version, language_id) in fixtures {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
Expand All @@ -1614,7 +1614,7 @@ mod tests {
config.tree.inject_config_file(config_file).await;
}
let resolver = LspResolver::default()
.with_new_config(&config, None, None)
.with_new_config(&config, cache, None, None)
.await;
StateSnapshot {
project_version: 0,
Expand Down
178 changes: 25 additions & 153 deletions cli/lsp/documents.rs
Expand Up @@ -12,7 +12,6 @@ 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;
Expand All @@ -32,7 +31,6 @@ 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;
Expand Down Expand Up @@ -716,64 +714,6 @@ 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>>,
Expand Down Expand Up @@ -918,21 +858,15 @@ 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,
}
Expand All @@ -945,29 +879,14 @@ 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
Expand Down Expand Up @@ -1102,11 +1021,14 @@ impl Documents {
let specifier = if let Ok(jsr_req_ref) =
JsrPackageReqReference::from_specifier(specifier)
{
Cow::Owned(self.jsr_resolver.jsr_to_registry_url(&jsr_req_ref)?)
Cow::Owned(self.resolver.jsr_to_registry_url(&jsr_req_ref)?)
} else {
Cow::Borrowed(specifier)
};
self.redirect_resolver.resolve(&specifier)
if !DOCUMENT_SCHEMES.contains(&specifier.scheme()) {
return None;
}
self.resolver.resolve_redirects(&specifier)
}
}

Expand Down Expand Up @@ -1279,7 +1201,8 @@ impl Documents {
results.push(None);
}
} else if let Some(specifier) = self
.resolve_imports_dependency(specifier)
.resolver
.resolve_graph_import(specifier)
.and_then(|r| r.maybe_specifier())
{
results.push(self.resolve_dependency(specifier, referrer));
Expand Down Expand Up @@ -1308,62 +1231,19 @@ 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);
Expand Down Expand Up @@ -1516,19 +1396,6 @@ 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(
Expand Down Expand Up @@ -1702,20 +1569,20 @@ mod tests {
use test_util::PathRef;
use test_util::TempDir;

fn setup(temp_dir: &TempDir) -> (Documents, PathRef) {
fn setup(temp_dir: &TempDir) -> (Documents, PathRef, Arc<dyn HttpCache>) {
let location = temp_dir.path().join("deps");
let cache = Arc::new(GlobalHttpCache::new(
location.to_path_buf(),
RealDenoCacheEnv,
));
let documents = Documents::new(cache);
(documents, location)
let documents = Documents::new(cache.clone());
(documents, location, cache)
}

#[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);
Expand All @@ -1741,7 +1608,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);
Expand Down Expand Up @@ -1785,7 +1652,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();
Expand Down Expand Up @@ -1813,7 +1680,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, cache) = setup(&temp_dir);
fs::create_dir_all(&documents_path).unwrap();

let file1_path = documents_path.join("file1.ts");
Expand Down Expand Up @@ -1862,9 +1729,14 @@ console.log(b, "hello deno");
.await;

let resolver = LspResolver::default()
.with_new_config(&config, None, None)
.with_new_config(&config, cache.clone(), None, None)
.await;
documents.update_config(&config, &resolver, &workspace_files);
documents.update_config(
&config,
&resolver,
cache.clone(),
&workspace_files,
);

// open the document
let document = documents.open(
Expand Down Expand Up @@ -1906,9 +1778,9 @@ console.log(b, "hello deno");
.await;

let resolver = LspResolver::default()
.with_new_config(&config, None, None)
.with_new_config(&config, cache.clone(), None, None)
.await;
documents.update_config(&config, &resolver, &workspace_files);
documents.update_config(&config, &resolver, cache, &workspace_files);

// check the document's dependencies
let document = documents.get(&file1_specifier).unwrap();
Expand Down