Skip to content

Commit

Permalink
feat(lsp): multi deno.json resolver scopes (denoland#24206)
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn committed Jun 17, 2024
1 parent 3419133 commit 5dec3fd
Show file tree
Hide file tree
Showing 10 changed files with 762 additions and 255 deletions.
82 changes: 63 additions & 19 deletions cli/lsp/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use deno_runtime::fs_util::specifier_to_file_path;

use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use std::collections::BTreeMap;
use std::fs;
use std::path::Path;
use std::sync::Arc;
Expand All @@ -29,13 +30,14 @@ pub const LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY: deno_cache_dir::GlobalToLocalCopy =
pub fn calculate_fs_version(
cache: &LspCache,
specifier: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<String> {
match specifier.scheme() {
"npm" | "node" | "data" | "blob" => None,
"file" => specifier_to_file_path(specifier)
.ok()
.and_then(|path| calculate_fs_version_at_path(&path)),
_ => calculate_fs_version_in_cache(cache, specifier),
_ => calculate_fs_version_in_cache(cache, specifier, file_referrer),
}
}

Expand All @@ -56,8 +58,9 @@ pub fn calculate_fs_version_at_path(path: &Path) -> Option<String> {
fn calculate_fs_version_in_cache(
cache: &LspCache,
specifier: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<String> {
let http_cache = cache.root_vendor_or_global();
let http_cache = cache.for_specifier(file_referrer);
let Ok(cache_key) = http_cache.cache_item_key(specifier) else {
return Some("1".to_string());
};
Expand All @@ -77,7 +80,7 @@ fn calculate_fs_version_in_cache(
pub struct LspCache {
deno_dir: DenoDir,
global: Arc<GlobalHttpCache>,
root_vendor: Option<Arc<LocalLspHttpCache>>,
vendors_by_scope: BTreeMap<ModuleSpecifier, Option<Arc<LocalLspHttpCache>>>,
}

impl Default for LspCache {
Expand Down Expand Up @@ -107,18 +110,24 @@ impl LspCache {
Self {
deno_dir,
global,
root_vendor: None,
vendors_by_scope: Default::default(),
}
}

pub fn update_config(&mut self, config: &Config) {
self.root_vendor = config.tree.root_data().and_then(|data| {
let vendor_dir = data.vendor_dir.as_ref()?;
Some(Arc::new(LocalLspHttpCache::new(
vendor_dir.clone(),
self.global.clone(),
)))
});
self.vendors_by_scope = config
.tree
.data_by_scope()
.iter()
.map(|(scope, config_data)| {
(
scope.clone(),
config_data.vendor_dir.as_ref().map(|v| {
Arc::new(LocalLspHttpCache::new(v.clone(), self.global.clone()))
}),
)
})
.collect();
}

pub fn deno_dir(&self) -> &DenoDir {
Expand All @@ -129,15 +138,50 @@ impl LspCache {
&self.global
}

pub fn root_vendor(&self) -> Option<&Arc<LocalLspHttpCache>> {
self.root_vendor.as_ref()
}

pub fn root_vendor_or_global(&self) -> Arc<dyn HttpCache> {
pub fn for_specifier(
&self,
file_referrer: Option<&ModuleSpecifier>,
) -> Arc<dyn HttpCache> {
let Some(file_referrer) = file_referrer else {
return self.global.clone();
};
self
.root_vendor
.as_ref()
.map(|v| v.clone() as _)
.vendors_by_scope
.iter()
.rfind(|(s, _)| file_referrer.as_str().starts_with(s.as_str()))
.and_then(|(_, v)| v.clone().map(|v| v as _))
.unwrap_or(self.global.clone() as _)
}

pub fn vendored_specifier(
&self,
specifier: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<ModuleSpecifier> {
let file_referrer = file_referrer?;
if !matches!(specifier.scheme(), "http" | "https") {
return None;
}
let vendor = self
.vendors_by_scope
.iter()
.rfind(|(s, _)| file_referrer.as_str().starts_with(s.as_str()))?
.1
.as_ref()?;
vendor.get_file_url(specifier)
}

pub fn unvendored_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Option<ModuleSpecifier> {
let path = specifier_to_file_path(specifier).ok()?;
let vendor = self
.vendors_by_scope
.iter()
.rfind(|(s, _)| specifier.as_str().starts_with(s.as_str()))?
.1
.as_ref()?;
vendor.get_remote_url(&path)
}
}
2 changes: 1 addition & 1 deletion cli/lsp/code_lens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ async fn resolve_references_code_lens(
locations.push(
reference
.entry
.to_location(asset_or_doc.line_index(), &language_server.url_map),
.to_location(asset_or_doc.line_index(), language_server),
);
}
Ok(locations)
Expand Down
85 changes: 39 additions & 46 deletions cli/lsp/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1568,27 +1568,13 @@ impl ConfigData {
#[derive(Clone, Debug, Default)]
pub struct ConfigTree {
first_folder: Option<ModuleSpecifier>,
scopes: Arc<BTreeMap<ModuleSpecifier, ConfigData>>,
scopes: Arc<BTreeMap<ModuleSpecifier, Arc<ConfigData>>>,
}

impl ConfigTree {
pub fn root_scope(&self) -> Option<&ModuleSpecifier> {
self.first_folder.as_ref()
}

pub fn root_data(&self) -> Option<&ConfigData> {
self.first_folder.as_ref().and_then(|s| self.scopes.get(s))
}

pub fn root_ts_config(&self) -> Arc<LspTsConfig> {
self
.root_data()
.map(|d| d.ts_config.clone())
.unwrap_or_default()
}

pub fn root_import_map(&self) -> Option<&Arc<ImportMap>> {
self.root_data().and_then(|d| d.import_map.as_ref())
let root_data = self.first_folder.as_ref().and_then(|s| self.scopes.get(s));
root_data.map(|d| d.ts_config.clone()).unwrap_or_default()
}

pub fn scope_for_specifier(
Expand All @@ -1599,19 +1585,20 @@ impl ConfigTree {
.scopes
.keys()
.rfind(|s| specifier.as_str().starts_with(s.as_str()))
.or(self.first_folder.as_ref())
}

pub fn data_for_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Option<&ConfigData> {
) -> Option<&Arc<ConfigData>> {
self
.scope_for_specifier(specifier)
.and_then(|s| self.scopes.get(s))
}

pub fn data_by_scope(&self) -> &Arc<BTreeMap<ModuleSpecifier, ConfigData>> {
pub fn data_by_scope(
&self,
) -> &Arc<BTreeMap<ModuleSpecifier, Arc<ConfigData>>> {
&self.scopes
}

Expand Down Expand Up @@ -1694,14 +1681,16 @@ impl ConfigTree {
if let Ok(config_uri) = folder_uri.join(config_path) {
scopes.insert(
folder_uri.clone(),
ConfigData::load(
Some(&config_uri),
folder_uri,
None,
settings,
Some(file_fetcher),
)
.await,
Arc::new(
ConfigData::load(
Some(&config_uri),
folder_uri,
None,
settings,
Some(file_fetcher),
)
.await,
),
);
}
}
Expand Down Expand Up @@ -1756,10 +1745,10 @@ impl ConfigTree {
Some(file_fetcher),
)
.await;
scopes.insert(member_scope.clone(), member_data);
scopes.insert(member_scope.clone(), Arc::new(member_data));
}
}
scopes.insert(scope, data);
scopes.insert(scope, Arc::new(data));
}

for folder_uri in settings.by_workspace_folder.keys() {
Expand All @@ -1769,14 +1758,16 @@ impl ConfigTree {
{
scopes.insert(
folder_uri.clone(),
ConfigData::load(
None,
folder_uri,
None,
settings,
Some(file_fetcher),
)
.await,
Arc::new(
ConfigData::load(
None,
folder_uri,
None,
settings,
Some(file_fetcher),
)
.await,
),
);
}
}
Expand All @@ -1787,14 +1778,16 @@ impl ConfigTree {
#[cfg(test)]
pub async fn inject_config_file(&mut self, config_file: ConfigFile) {
let scope = config_file.specifier.join(".").unwrap();
let data = ConfigData::load_inner(
Some(config_file),
&scope,
None,
&Default::default(),
None,
)
.await;
let data = Arc::new(
ConfigData::load_inner(
Some(config_file),
&scope,
None,
&Default::default(),
None,
)
.await,
);
self.first_folder = Some(scope.clone());
self.scopes = Arc::new([(scope, data)].into_iter().collect());
}
Expand Down
19 changes: 16 additions & 3 deletions cli/lsp/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::client::Client;
use super::config::Config;
use super::documents;
use super::documents::Document;
use super::documents::Documents;
use super::documents::DocumentsFilter;
use super::language_server;
use super::language_server::StateSnapshot;
Expand Down Expand Up @@ -120,6 +121,7 @@ impl DiagnosticsPublisher {
source: DiagnosticSource,
diagnostics: DiagnosticVec,
url_map: &LspUrlMap,
documents: &Documents,
token: &CancellationToken,
) -> usize {
let mut diagnostics_by_specifier =
Expand Down Expand Up @@ -153,11 +155,12 @@ impl DiagnosticsPublisher {
self
.state
.update(&record.specifier, version, &all_specifier_diagnostics);
let file_referrer = documents.get_file_referrer(&record.specifier);
self
.client
.publish_diagnostics(
url_map
.normalize_specifier(&record.specifier)
.normalize_specifier(&record.specifier, file_referrer.as_deref())
.unwrap_or(LspClientUrl::new(record.specifier)),
all_specifier_diagnostics,
version,
Expand All @@ -183,11 +186,12 @@ impl DiagnosticsPublisher {
if let Some(removed_value) = maybe_removed_value {
// clear out any diagnostics for this specifier
self.state.update(specifier, removed_value.version, &[]);
let file_referrer = documents.get_file_referrer(specifier);
self
.client
.publish_diagnostics(
url_map
.normalize_specifier(specifier)
.normalize_specifier(specifier, file_referrer.as_deref())
.unwrap_or_else(|_| LspClientUrl::new(specifier.clone())),
Vec::new(),
removed_value.version,
Expand Down Expand Up @@ -519,6 +523,7 @@ impl DiagnosticsServer {
DiagnosticSource::Ts,
diagnostics,
&url_map,
snapshot.documents.as_ref(),
&token,
)
.await;
Expand Down Expand Up @@ -556,6 +561,7 @@ impl DiagnosticsServer {
let mark = performance.mark("lsp.update_diagnostics_deps");
let diagnostics = spawn_blocking({
let token = token.clone();
let snapshot = snapshot.clone();
move || generate_deno_diagnostics(&snapshot, &config, token)
})
.await
Expand All @@ -568,6 +574,7 @@ impl DiagnosticsServer {
DiagnosticSource::Deno,
diagnostics,
&url_map,
snapshot.documents.as_ref(),
&token,
)
.await;
Expand Down Expand Up @@ -605,6 +612,7 @@ impl DiagnosticsServer {
let mark = performance.mark("lsp.update_diagnostics_lint");
let diagnostics = spawn_blocking({
let token = token.clone();
let snapshot = snapshot.clone();
move || generate_lint_diagnostics(&snapshot, &config, token)
})
.await
Expand All @@ -617,6 +625,7 @@ impl DiagnosticsServer {
DiagnosticSource::Lint,
diagnostics,
&url_map,
snapshot.documents.as_ref(),
&token,
)
.await;
Expand Down Expand Up @@ -1466,7 +1475,11 @@ fn diagnose_dependency(
return; // ignore, surface typescript errors instead
}

let import_map = snapshot.config.tree.root_import_map();
let import_map = snapshot
.config
.tree
.data_for_specifier(referrer_doc.file_referrer().unwrap_or(referrer))
.and_then(|d| d.import_map.as_ref());
if let Some(import_map) = import_map {
if let Resolution::Ok(resolved) = &dependency.maybe_code {
if let Some(to) = import_map.lookup(&resolved.specifier, referrer) {
Expand Down

0 comments on commit 5dec3fd

Please sign in to comment.