mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
feat(lsp): multi deno.json resolver scopes (#24206)
This commit is contained in:
parent
10828cd62c
commit
b318d51822
10 changed files with 762 additions and 255 deletions
|
@ -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;
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -1694,6 +1681,7 @@ impl ConfigTree {
|
|||
if let Ok(config_uri) = folder_uri.join(config_path) {
|
||||
scopes.insert(
|
||||
folder_uri.clone(),
|
||||
Arc::new(
|
||||
ConfigData::load(
|
||||
Some(&config_uri),
|
||||
folder_uri,
|
||||
|
@ -1702,6 +1690,7 @@ impl ConfigTree {
|
|||
Some(file_fetcher),
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -1769,6 +1758,7 @@ impl ConfigTree {
|
|||
{
|
||||
scopes.insert(
|
||||
folder_uri.clone(),
|
||||
Arc::new(
|
||||
ConfigData::load(
|
||||
None,
|
||||
folder_uri,
|
||||
|
@ -1777,6 +1767,7 @@ impl ConfigTree {
|
|||
Some(file_fetcher),
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
let data = Arc::new(
|
||||
ConfigData::load_inner(
|
||||
Some(config_file),
|
||||
&scope,
|
||||
None,
|
||||
&Default::default(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
.await,
|
||||
);
|
||||
self.first_folder = Some(scope.clone());
|
||||
self.scopes = Arc::new([(scope, data)].into_iter().collect());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -120,6 +121,7 @@ impl DiagnosticsPublisher {
|
|||
source: DiagnosticSource,
|
||||
diagnostics: DiagnosticVec,
|
||||
url_map: &LspUrlMap,
|
||||
documents: &Documents,
|
||||
token: &CancellationToken,
|
||||
) -> usize {
|
||||
let mut diagnostics_by_specifier =
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -519,6 +523,7 @@ impl DiagnosticsServer {
|
|||
DiagnosticSource::Ts,
|
||||
diagnostics,
|
||||
&url_map,
|
||||
snapshot.documents.as_ref(),
|
||||
&token,
|
||||
)
|
||||
.await;
|
||||
|
@ -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
|
||||
|
@ -568,6 +574,7 @@ impl DiagnosticsServer {
|
|||
DiagnosticSource::Deno,
|
||||
diagnostics,
|
||||
&url_map,
|
||||
snapshot.documents.as_ref(),
|
||||
&token,
|
||||
)
|
||||
.await;
|
||||
|
@ -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
|
||||
|
@ -617,6 +625,7 @@ impl DiagnosticsServer {
|
|||
DiagnosticSource::Lint,
|
||||
diagnostics,
|
||||
&url_map,
|
||||
snapshot.documents.as_ref(),
|
||||
&token,
|
||||
)
|
||||
.await;
|
||||
|
@ -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) {
|
||||
|
|
|
@ -303,6 +303,10 @@ impl Document {
|
|||
cache: &Arc<LspCache>,
|
||||
file_referrer: Option<ModuleSpecifier>,
|
||||
) -> Arc<Self> {
|
||||
let file_referrer = Some(&specifier)
|
||||
.filter(|s| s.scheme() == "file")
|
||||
.cloned()
|
||||
.or(file_referrer);
|
||||
let media_type = resolve_media_type(
|
||||
&specifier,
|
||||
maybe_headers.as_ref(),
|
||||
|
@ -336,9 +340,13 @@ impl Document {
|
|||
Arc::new(Self {
|
||||
config,
|
||||
dependencies,
|
||||
file_referrer: file_referrer.filter(|_| specifier.scheme() != "file"),
|
||||
maybe_fs_version: calculate_fs_version(
|
||||
cache,
|
||||
&specifier,
|
||||
file_referrer.as_ref(),
|
||||
),
|
||||
file_referrer,
|
||||
maybe_types_dependency,
|
||||
maybe_fs_version: calculate_fs_version(cache, &specifier),
|
||||
line_index,
|
||||
maybe_language_id,
|
||||
maybe_headers,
|
||||
|
@ -540,7 +548,11 @@ impl Document {
|
|||
config: self.config.clone(),
|
||||
specifier: self.specifier.clone(),
|
||||
file_referrer: self.file_referrer.clone(),
|
||||
maybe_fs_version: calculate_fs_version(cache, &self.specifier),
|
||||
maybe_fs_version: calculate_fs_version(
|
||||
cache,
|
||||
&self.specifier,
|
||||
self.file_referrer.as_ref(),
|
||||
),
|
||||
maybe_language_id: self.maybe_language_id,
|
||||
dependencies: self.dependencies.clone(),
|
||||
maybe_types_dependency: self.maybe_types_dependency.clone(),
|
||||
|
@ -563,7 +575,11 @@ impl Document {
|
|||
config: self.config.clone(),
|
||||
specifier: self.specifier.clone(),
|
||||
file_referrer: self.file_referrer.clone(),
|
||||
maybe_fs_version: calculate_fs_version(cache, &self.specifier),
|
||||
maybe_fs_version: calculate_fs_version(
|
||||
cache,
|
||||
&self.specifier,
|
||||
self.file_referrer.as_ref(),
|
||||
),
|
||||
maybe_language_id: self.maybe_language_id,
|
||||
dependencies: self.dependencies.clone(),
|
||||
maybe_types_dependency: self.maybe_types_dependency.clone(),
|
||||
|
@ -766,7 +782,10 @@ impl FileSystemDocuments {
|
|||
cache: &Arc<LspCache>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Arc<Document>> {
|
||||
let new_fs_version = calculate_fs_version(cache, specifier);
|
||||
let file_referrer = Some(specifier)
|
||||
.filter(|s| s.scheme() == "file")
|
||||
.or(file_referrer);
|
||||
let new_fs_version = calculate_fs_version(cache, specifier, file_referrer);
|
||||
let old_doc = self.docs.get(specifier).map(|v| v.value().clone());
|
||||
let dirty = match &old_doc {
|
||||
None => true,
|
||||
|
@ -830,7 +849,7 @@ impl FileSystemDocuments {
|
|||
file_referrer.cloned(),
|
||||
)
|
||||
} else {
|
||||
let http_cache = cache.root_vendor_or_global();
|
||||
let http_cache = cache.for_specifier(file_referrer);
|
||||
let cache_key = http_cache.cache_item_key(specifier).ok()?;
|
||||
let bytes = http_cache
|
||||
.read_file_bytes(&cache_key, None, LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY)
|
||||
|
@ -1089,7 +1108,7 @@ impl Documents {
|
|||
.map(|p| p.is_file())
|
||||
.unwrap_or(false);
|
||||
}
|
||||
if self.cache.root_vendor_or_global().contains(&specifier) {
|
||||
if self.cache.for_specifier(file_referrer).contains(&specifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1335,8 +1354,7 @@ impl Documents {
|
|||
let mut visit_doc = |doc: &Arc<Document>| {
|
||||
let scope = doc
|
||||
.file_referrer()
|
||||
.and_then(|r| self.config.tree.scope_for_specifier(r))
|
||||
.or(self.config.tree.root_scope());
|
||||
.and_then(|r| self.config.tree.scope_for_specifier(r));
|
||||
let reqs = npm_reqs_by_scope.entry(scope.cloned()).or_default();
|
||||
for dependency in doc.dependencies().values() {
|
||||
if let Some(dep) = dependency.get_code() {
|
||||
|
@ -1367,16 +1385,9 @@ impl Documents {
|
|||
}
|
||||
|
||||
// fill the reqs from the lockfile
|
||||
// TODO(nayeemrmn): Iterate every lockfile here for multi-deno.json.
|
||||
if let Some(lockfile) = self
|
||||
.config
|
||||
.tree
|
||||
.root_data()
|
||||
.and_then(|d| d.lockfile.as_ref())
|
||||
{
|
||||
let reqs = npm_reqs_by_scope
|
||||
.entry(self.config.tree.root_scope().cloned())
|
||||
.or_default();
|
||||
for (scope, config_data) in self.config.tree.data_by_scope().as_ref() {
|
||||
if let Some(lockfile) = config_data.lockfile.as_ref() {
|
||||
let reqs = npm_reqs_by_scope.entry(Some(scope.clone())).or_default();
|
||||
let lockfile = lockfile.lock();
|
||||
for key in lockfile.content.packages.specifiers.keys() {
|
||||
if let Some(key) = key.strip_prefix("npm:") {
|
||||
|
@ -1386,6 +1397,7 @@ impl Documents {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a @types/node package exists when any module uses a node: specifier.
|
||||
// Unlike on the command line, here we just add @types/node to the npm package
|
||||
|
|
|
@ -682,7 +682,7 @@ impl Inner {
|
|||
pub fn update_cache(&mut self) {
|
||||
let mark = self.performance.mark("lsp.update_cache");
|
||||
self.cache.update_config(&self.config);
|
||||
self.url_map.set_cache(self.cache.root_vendor().cloned());
|
||||
self.url_map.set_cache(&self.cache);
|
||||
self.performance.measure(mark);
|
||||
}
|
||||
|
||||
|
@ -1134,11 +1134,9 @@ impl Inner {
|
|||
let package_reqs = self.documents.npm_reqs_by_scope();
|
||||
let resolver = self.resolver.clone();
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let handle =
|
||||
spawn(async move { resolver.set_npm_reqs(&package_reqs).await });
|
||||
if let Err(err) = handle.await.unwrap() {
|
||||
lsp_warn!("Could not set npm package requirements. {:#}", err);
|
||||
}
|
||||
spawn(async move { resolver.set_npm_reqs(&package_reqs).await })
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
async fn did_close(&mut self, params: DidCloseTextDocumentParams) {
|
||||
|
@ -1818,11 +1816,15 @@ impl Inner {
|
|||
|
||||
pub fn get_ts_response_import_mapper(
|
||||
&self,
|
||||
_referrer: &ModuleSpecifier,
|
||||
file_referrer: &ModuleSpecifier,
|
||||
) -> TsResponseImportMapper {
|
||||
TsResponseImportMapper::new(
|
||||
&self.documents,
|
||||
self.config.tree.root_import_map().map(|i| i.as_ref()),
|
||||
self
|
||||
.config
|
||||
.tree
|
||||
.data_for_specifier(file_referrer)
|
||||
.and_then(|d| d.import_map.as_ref().map(|i| i.as_ref())),
|
||||
self.resolver.as_ref(),
|
||||
)
|
||||
}
|
||||
|
@ -1999,11 +2001,7 @@ impl Inner {
|
|||
self.get_asset_or_document(&reference_specifier)?;
|
||||
asset_or_doc.line_index()
|
||||
};
|
||||
results.push(
|
||||
reference
|
||||
.entry
|
||||
.to_location(reference_line_index, &self.url_map),
|
||||
);
|
||||
results.push(reference.entry.to_location(reference_line_index, self));
|
||||
}
|
||||
|
||||
self.performance.measure(mark);
|
||||
|
@ -2125,6 +2123,10 @@ impl Inner {
|
|||
.map(|s| s.suggest.include_completions_for_import_statements)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let file_referrer = asset_or_doc
|
||||
.document()
|
||||
.and_then(|d| d.file_referrer())
|
||||
.unwrap_or(&specifier);
|
||||
response = completions::get_import_completions(
|
||||
&specifier,
|
||||
¶ms.text_document_position.position,
|
||||
|
@ -2135,7 +2137,11 @@ impl Inner {
|
|||
&self.npm_search_api,
|
||||
&self.documents,
|
||||
self.resolver.as_ref(),
|
||||
self.config.tree.root_import_map().map(|i| i.as_ref()),
|
||||
self
|
||||
.config
|
||||
.tree
|
||||
.data_for_specifier(file_referrer)
|
||||
.and_then(|d| d.import_map.as_ref().map(|i| i.as_ref())),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
@ -3442,7 +3448,7 @@ impl Inner {
|
|||
let mark = self
|
||||
.performance
|
||||
.mark_with_args("lsp.cache", (&specifiers, &referrer));
|
||||
let config_data = self.config.tree.root_data();
|
||||
let config_data = self.config.tree.data_for_specifier(&referrer);
|
||||
let mut roots = if !specifiers.is_empty() {
|
||||
specifiers
|
||||
} else {
|
||||
|
@ -3451,16 +3457,17 @@ impl Inner {
|
|||
|
||||
// always include the npm packages since resolution of one npm package
|
||||
// might affect the resolution of other npm packages
|
||||
roots.extend(
|
||||
self
|
||||
if let Some(npm_reqs) = self
|
||||
.documents
|
||||
.npm_reqs_by_scope()
|
||||
.values()
|
||||
.flatten()
|
||||
.collect::<BTreeSet<_>>()
|
||||
.get(&config_data.map(|d| d.scope.clone()))
|
||||
{
|
||||
roots.extend(
|
||||
npm_reqs
|
||||
.iter()
|
||||
.map(|req| ModuleSpecifier::parse(&format!("npm:{}", req)).unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
let workspace_settings = self.config.workspace_settings();
|
||||
let cli_options = CliOptions::new(
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::graph_util::CliJsrUrlProvider;
|
|||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::Config;
|
||||
use crate::lsp::config::ConfigData;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::npm::create_cli_npm_resolver_for_lsp;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverByonmCreateOptions;
|
||||
|
@ -54,17 +55,17 @@ use super::cache::LspCache;
|
|||
use super::jsr::JsrCacheResolver;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LspResolver {
|
||||
struct LspScopeResolver {
|
||||
graph_resolver: Arc<CliGraphResolver>,
|
||||
jsr_resolver: Option<Arc<JsrCacheResolver>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
redirect_resolver: Option<Arc<RedirectResolver>>,
|
||||
graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
||||
config: Arc<Config>,
|
||||
config_data: Option<Arc<ConfigData>>,
|
||||
}
|
||||
|
||||
impl Default for LspResolver {
|
||||
impl Default for LspScopeResolver {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
graph_resolver: create_graph_resolver(None, None, None),
|
||||
|
@ -73,38 +74,41 @@ impl Default for LspResolver {
|
|||
node_resolver: None,
|
||||
redirect_resolver: None,
|
||||
graph_imports: Default::default(),
|
||||
config: Default::default(),
|
||||
config_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LspResolver {
|
||||
pub async fn from_config(
|
||||
impl LspScopeResolver {
|
||||
async fn from_config_data(
|
||||
config_data: Option<&Arc<ConfigData>>,
|
||||
config: &Config,
|
||||
cache: &LspCache,
|
||||
http_client_provider: Option<&Arc<HttpClientProvider>>,
|
||||
) -> Self {
|
||||
let config_data = config.tree.root_data();
|
||||
let mut npm_resolver = None;
|
||||
let mut node_resolver = None;
|
||||
if let (Some(http_client), Some(config_data)) =
|
||||
(http_client_provider, config_data)
|
||||
{
|
||||
npm_resolver = create_npm_resolver(config_data, cache, http_client).await;
|
||||
if let Some(http_client) = http_client_provider {
|
||||
npm_resolver = create_npm_resolver(
|
||||
config_data.map(|d| d.as_ref()),
|
||||
cache,
|
||||
http_client,
|
||||
)
|
||||
.await;
|
||||
node_resolver = create_node_resolver(npm_resolver.as_ref());
|
||||
}
|
||||
let graph_resolver = create_graph_resolver(
|
||||
config_data,
|
||||
config_data.map(|d| d.as_ref()),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
let jsr_resolver = Some(Arc::new(JsrCacheResolver::new(
|
||||
cache.root_vendor_or_global(),
|
||||
config_data,
|
||||
cache.for_specifier(config_data.map(|d| &d.scope)),
|
||||
config_data.map(|d| d.as_ref()),
|
||||
config,
|
||||
)));
|
||||
let redirect_resolver = Some(Arc::new(RedirectResolver::new(
|
||||
cache.root_vendor_or_global(),
|
||||
cache.for_specifier(config_data.map(|d| &d.scope)),
|
||||
)));
|
||||
let npm_graph_resolver = graph_resolver.create_graph_npm_resolver();
|
||||
let graph_imports = config_data
|
||||
|
@ -135,16 +139,16 @@ impl LspResolver {
|
|||
node_resolver,
|
||||
redirect_resolver,
|
||||
graph_imports,
|
||||
config: Arc::new(config.clone()),
|
||||
config_data: config_data.cloned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Arc<Self> {
|
||||
fn snapshot(&self) -> Arc<Self> {
|
||||
let npm_resolver =
|
||||
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
|
||||
let node_resolver = create_node_resolver(npm_resolver.as_ref());
|
||||
let graph_resolver = create_graph_resolver(
|
||||
self.config.tree.root_data(),
|
||||
self.config_data.as_deref(),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
|
@ -155,61 +159,125 @@ impl LspResolver {
|
|||
node_resolver,
|
||||
redirect_resolver: self.redirect_resolver.clone(),
|
||||
graph_imports: self.graph_imports.clone(),
|
||||
config: self.config.clone(),
|
||||
config_data: self.config_data.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LspResolver {
|
||||
unscoped: Arc<LspScopeResolver>,
|
||||
by_scope: BTreeMap<ModuleSpecifier, Arc<LspScopeResolver>>,
|
||||
}
|
||||
|
||||
impl LspResolver {
|
||||
pub async fn from_config(
|
||||
config: &Config,
|
||||
cache: &LspCache,
|
||||
http_client_provider: Option<&Arc<HttpClientProvider>>,
|
||||
) -> Self {
|
||||
let mut by_scope = BTreeMap::new();
|
||||
for (scope, config_data) in config.tree.data_by_scope().as_ref() {
|
||||
by_scope.insert(
|
||||
scope.clone(),
|
||||
Arc::new(
|
||||
LspScopeResolver::from_config_data(
|
||||
Some(config_data),
|
||||
config,
|
||||
cache,
|
||||
http_client_provider,
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
Self {
|
||||
unscoped: Arc::new(
|
||||
LspScopeResolver::from_config_data(
|
||||
None,
|
||||
config,
|
||||
cache,
|
||||
http_client_provider,
|
||||
)
|
||||
.await,
|
||||
),
|
||||
by_scope,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
unscoped: self.unscoped.snapshot(),
|
||||
by_scope: self
|
||||
.by_scope
|
||||
.iter()
|
||||
.map(|(s, r)| (s.clone(), r.snapshot()))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn did_cache(&self) {
|
||||
self.jsr_resolver.as_ref().inspect(|r| r.did_cache());
|
||||
for resolver in
|
||||
std::iter::once(&self.unscoped).chain(self.by_scope.values())
|
||||
{
|
||||
resolver.jsr_resolver.as_ref().inspect(|r| r.did_cache());
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_npm_reqs(
|
||||
&self,
|
||||
reqs: &BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>,
|
||||
) -> Result<(), AnyError> {
|
||||
let reqs = reqs
|
||||
.values()
|
||||
.flatten()
|
||||
.collect::<BTreeSet<_>>()
|
||||
) {
|
||||
for (scope, resolver) in [(None, &self.unscoped)]
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_ref() {
|
||||
.chain(self.by_scope.iter().map(|(s, r)| (Some(s), r)))
|
||||
{
|
||||
if let Some(npm_resolver) = resolver.npm_resolver.as_ref() {
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
return npm_resolver.set_package_reqs(&reqs).await;
|
||||
let reqs = reqs
|
||||
.get(&scope.cloned())
|
||||
.map(|reqs| reqs.iter().cloned().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
if let Err(err) = npm_resolver.set_package_reqs(&reqs).await {
|
||||
lsp_warn!("Could not set npm package requirements: {:#}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_graph_resolver(
|
||||
&self,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> &dyn Resolver {
|
||||
self.graph_resolver.as_ref()
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.graph_resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn create_graph_npm_resolver(
|
||||
&self,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
self.graph_resolver.create_graph_npm_resolver()
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.graph_resolver.create_graph_npm_resolver()
|
||||
}
|
||||
|
||||
pub fn maybe_managed_npm_resolver(
|
||||
&self,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<&ManagedCliNpmResolver> {
|
||||
self.npm_resolver.as_ref().and_then(|r| r.as_managed())
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.npm_resolver.as_ref().and_then(|r| r.as_managed())
|
||||
}
|
||||
|
||||
pub fn graph_imports_by_referrer(
|
||||
&self,
|
||||
) -> IndexMap<&ModuleSpecifier, Vec<&ModuleSpecifier>> {
|
||||
self
|
||||
.graph_imports
|
||||
.by_scope
|
||||
.iter()
|
||||
.map(|(s, i)| {
|
||||
.flat_map(|(_, r)| {
|
||||
r.graph_imports.iter().map(|(s, i)| {
|
||||
(
|
||||
s,
|
||||
i.dependencies
|
||||
|
@ -218,41 +286,49 @@ impl LspResolver {
|
|||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn jsr_to_resource_url(
|
||||
&self,
|
||||
req_ref: &JsrPackageReqReference,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<ModuleSpecifier> {
|
||||
self.jsr_resolver.as_ref()?.jsr_to_resource_url(req_ref)
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.jsr_resolver.as_ref()?.jsr_to_resource_url(req_ref)
|
||||
}
|
||||
|
||||
pub fn jsr_lookup_export_for_path(
|
||||
&self,
|
||||
nv: &PackageNv,
|
||||
path: &str,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<String> {
|
||||
self.jsr_resolver.as_ref()?.lookup_export_for_path(nv, path)
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver
|
||||
.jsr_resolver
|
||||
.as_ref()?
|
||||
.lookup_export_for_path(nv, path)
|
||||
}
|
||||
|
||||
pub fn jsr_lookup_req_for_nv(
|
||||
&self,
|
||||
nv: &PackageNv,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<PackageReq> {
|
||||
self.jsr_resolver.as_ref()?.lookup_req_for_nv(nv)
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.jsr_resolver.as_ref()?.lookup_req_for_nv(nv)
|
||||
}
|
||||
|
||||
pub fn npm_to_file_url(
|
||||
&self,
|
||||
req_ref: &NpmPackageReqReference,
|
||||
referrer: &ModuleSpecifier,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
let node_resolver = self.node_resolver.as_ref()?;
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let node_resolver = resolver.node_resolver.as_ref()?;
|
||||
Some(NodeResolution::into_specifier_and_media_type(
|
||||
node_resolver
|
||||
.resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types)
|
||||
|
@ -261,7 +337,8 @@ impl LspResolver {
|
|||
}
|
||||
|
||||
pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if let Some(npm_resolver) = &self.npm_resolver {
|
||||
let resolver = self.get_scope_resolver(Some(specifier));
|
||||
if let Some(npm_resolver) = &resolver.npm_resolver {
|
||||
return npm_resolver.in_npm_package(specifier);
|
||||
}
|
||||
false
|
||||
|
@ -271,7 +348,8 @@ impl LspResolver {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<MediaType> {
|
||||
let node_resolver = self.node_resolver.as_ref()?;
|
||||
let resolver = self.get_scope_resolver(Some(specifier));
|
||||
let node_resolver = resolver.node_resolver.as_ref()?;
|
||||
let resolution = node_resolver
|
||||
.url_to_node_resolution(specifier.clone())
|
||||
.ok()?;
|
||||
|
@ -282,7 +360,8 @@ impl LspResolver {
|
|||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<Option<Rc<PackageJson>>, AnyError> {
|
||||
let Some(node_resolver) = self.node_resolver.as_ref() else {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(node_resolver) = resolver.node_resolver.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
node_resolver.get_closest_package_json(
|
||||
|
@ -294,9 +373,10 @@ impl LspResolver {
|
|||
pub fn resolve_redirects(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<ModuleSpecifier> {
|
||||
let Some(redirect_resolver) = self.redirect_resolver.as_ref() else {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let Some(redirect_resolver) = resolver.redirect_resolver.as_ref() else {
|
||||
return Some(specifier.clone());
|
||||
};
|
||||
redirect_resolver.resolve(specifier)
|
||||
|
@ -305,9 +385,10 @@ impl LspResolver {
|
|||
pub fn redirect_chain_headers(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
_file_referrer: Option<&ModuleSpecifier>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Vec<(ModuleSpecifier, Arc<HashMap<String, String>>)> {
|
||||
let Some(redirect_resolver) = self.redirect_resolver.as_ref() else {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let Some(redirect_resolver) = resolver.redirect_resolver.as_ref() else {
|
||||
return vec![];
|
||||
};
|
||||
redirect_resolver
|
||||
|
@ -316,26 +397,47 @@ impl LspResolver {
|
|||
.map(|(s, e)| (s, e.headers.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_scope_resolver(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> &LspScopeResolver {
|
||||
let Some(file_referrer) = file_referrer else {
|
||||
return self.unscoped.as_ref();
|
||||
};
|
||||
self
|
||||
.by_scope
|
||||
.iter()
|
||||
.rfind(|(s, _)| file_referrer.as_str().starts_with(s.as_str()))
|
||||
.map(|(_, r)| r.as_ref())
|
||||
.unwrap_or(self.unscoped.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_npm_resolver(
|
||||
config_data: &ConfigData,
|
||||
config_data: Option<&ConfigData>,
|
||||
cache: &LspCache,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
) -> Option<Arc<dyn CliNpmResolver>> {
|
||||
let node_modules_dir = config_data
|
||||
.node_modules_dir
|
||||
.clone()
|
||||
.or_else(|| specifier_to_file_path(&config_data.scope).ok())?;
|
||||
let options = if config_data.byonm {
|
||||
let mut byonm_dir = None;
|
||||
if let Some(config_data) = config_data {
|
||||
if config_data.byonm {
|
||||
byonm_dir = Some(config_data.node_modules_dir.clone().or_else(|| {
|
||||
specifier_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})?)
|
||||
}
|
||||
}
|
||||
let options = if let Some(byonm_dir) = byonm_dir {
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
root_node_modules_dir: node_modules_dir.clone(),
|
||||
root_node_modules_dir: byonm_dir,
|
||||
})
|
||||
} else {
|
||||
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
snapshot: match config_data.lockfile.as_ref() {
|
||||
snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
|
@ -354,15 +456,17 @@ async fn create_npm_resolver(
|
|||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: config_data.node_modules_dir.clone(),
|
||||
maybe_node_modules_path: config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
package_json_deps_provider: Arc::new(PackageJsonDepsProvider::new(
|
||||
config_data.package_json.as_ref().map(|package_json| {
|
||||
config_data
|
||||
.and_then(|d| d.package_json.as_ref())
|
||||
.map(|package_json| {
|
||||
package_json::get_local_package_json_version_reqs(package_json)
|
||||
}),
|
||||
)),
|
||||
npmrc: config_data
|
||||
.npmrc
|
||||
.clone()
|
||||
.and_then(|d| d.npmrc.clone())
|
||||
.unwrap_or_else(create_default_npmrc),
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
})
|
||||
|
|
|
@ -19,7 +19,6 @@ use super::semantic_tokens;
|
|||
use super::semantic_tokens::SemanticTokensBuilder;
|
||||
use super::text::LineIndex;
|
||||
use super::urls::LspClientUrl;
|
||||
use super::urls::LspUrlMap;
|
||||
use super::urls::INVALID_SPECIFIER;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
|
@ -1844,9 +1843,12 @@ impl DocumentSpan {
|
|||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
let target_line_index = target_asset_or_doc.line_index();
|
||||
let file_referrer = language_server
|
||||
.documents
|
||||
.get_file_referrer(&target_specifier);
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier)
|
||||
.normalize_specifier(&target_specifier, file_referrer.as_deref())
|
||||
.ok()?;
|
||||
let (target_range, target_selection_range) =
|
||||
if let Some(context_span) = &self.context_span {
|
||||
|
@ -1890,9 +1892,10 @@ impl DocumentSpan {
|
|||
language_server.get_maybe_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let mut target = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier)
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.ok()?
|
||||
.into_url();
|
||||
target.set_fragment(Some(&format!(
|
||||
|
@ -1950,9 +1953,10 @@ impl NavigateToItem {
|
|||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).ok()?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier)
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.ok()?;
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let location = lsp::Location {
|
||||
|
@ -2208,9 +2212,10 @@ impl ImplementationLocation {
|
|||
) -> lsp::Location {
|
||||
let specifier = resolve_url(&self.document_span.file_name)
|
||||
.unwrap_or_else(|_| ModuleSpecifier::parse("deno://invalid").unwrap());
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier)
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| {
|
||||
LspClientUrl::new(ModuleSpecifier::parse("deno://invalid").unwrap())
|
||||
});
|
||||
|
@ -2270,7 +2275,11 @@ impl RenameLocations {
|
|||
includes_non_files = true;
|
||||
continue;
|
||||
}
|
||||
let uri = language_server.url_map.normalize_specifier(&specifier)?;
|
||||
let file_referrer =
|
||||
language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
|
||||
// ensure TextDocumentEdit for `location.file_name`.
|
||||
|
@ -2916,12 +2925,14 @@ impl ReferenceEntry {
|
|||
pub fn to_location(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
url_map: &LspUrlMap,
|
||||
language_server: &language_server::Inner,
|
||||
) -> lsp::Location {
|
||||
let specifier = resolve_url(&self.document_span.file_name)
|
||||
.unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
||||
let uri = url_map
|
||||
.normalize_specifier(&specifier)
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
lsp::Location {
|
||||
uri: uri.into_url(),
|
||||
|
@ -2977,9 +2988,12 @@ impl CallHierarchyItem {
|
|||
) -> lsp::CallHierarchyItem {
|
||||
let target_specifier =
|
||||
resolve_url(&self.file).unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
||||
let file_referrer = language_server
|
||||
.documents
|
||||
.get_file_referrer(&target_specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier)
|
||||
.normalize_specifier(&target_specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
|
||||
let use_file_name = self.is_source_file_item();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::cache::LocalLspHttpCache;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -12,6 +10,8 @@ use once_cell::sync::Lazy;
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::cache::LspCache;
|
||||
|
||||
/// Used in situations where a default URL needs to be used where otherwise a
|
||||
/// panic is undesired.
|
||||
pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> =
|
||||
|
@ -156,13 +156,13 @@ pub enum LspUrlKind {
|
|||
/// to allow the Deno language server to manage these as virtual documents.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LspUrlMap {
|
||||
local_http_cache: Option<Arc<LocalLspHttpCache>>,
|
||||
cache: LspCache,
|
||||
inner: Arc<Mutex<LspUrlMapInner>>,
|
||||
}
|
||||
|
||||
impl LspUrlMap {
|
||||
pub fn set_cache(&mut self, http_cache: Option<Arc<LocalLspHttpCache>>) {
|
||||
self.local_http_cache = http_cache;
|
||||
pub fn set_cache(&mut self, cache: &LspCache) {
|
||||
self.cache = cache.clone();
|
||||
}
|
||||
|
||||
/// Normalize a specifier that is used internally within Deno (or tsc) to a
|
||||
|
@ -170,14 +170,13 @@ impl LspUrlMap {
|
|||
pub fn normalize_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<LspClientUrl, AnyError> {
|
||||
if let Some(cache) = &self.local_http_cache {
|
||||
if matches!(specifier.scheme(), "http" | "https") {
|
||||
if let Some(file_url) = cache.get_file_url(specifier) {
|
||||
if let Some(file_url) =
|
||||
self.cache.vendored_specifier(specifier, file_referrer)
|
||||
{
|
||||
return Ok(LspClientUrl(file_url));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(url) = inner.get_url(specifier).cloned() {
|
||||
Ok(url)
|
||||
|
@ -220,15 +219,9 @@ impl LspUrlMap {
|
|||
/// so we need to force it to in the mapping and nee to explicitly state whether
|
||||
/// this is a file or directory url.
|
||||
pub fn normalize_url(&self, url: &Url, kind: LspUrlKind) -> ModuleSpecifier {
|
||||
if let Some(cache) = &self.local_http_cache {
|
||||
if url.scheme() == "file" {
|
||||
if let Ok(path) = url.to_file_path() {
|
||||
if let Some(remote_url) = cache.get_remote_url(&path) {
|
||||
if let Some(remote_url) = self.cache.unvendored_specifier(url) {
|
||||
return remote_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(specifier) = inner.get_specifier(url).cloned() {
|
||||
return specifier;
|
||||
|
@ -296,7 +289,7 @@ mod tests {
|
|||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture)
|
||||
.normalize_specifier(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
|
@ -318,7 +311,7 @@ mod tests {
|
|||
assert_eq!(&actual_specifier, &expected_specifier);
|
||||
|
||||
let actual_url = map
|
||||
.normalize_specifier(&actual_specifier)
|
||||
.normalize_specifier(&actual_specifier, None)
|
||||
.unwrap()
|
||||
.as_url()
|
||||
.clone();
|
||||
|
@ -331,7 +324,7 @@ mod tests {
|
|||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://cdn.skypack.dev/-/postcss@v8.2.9-E4SktPp9c0AtxrJHp8iV/dist=es2020,mode=types/lib/postcss.d.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture)
|
||||
.normalize_specifier(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
@ -346,7 +339,7 @@ mod tests {
|
|||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture)
|
||||
.normalize_specifier(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
@ -361,7 +354,7 @@ mod tests {
|
|||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("http://localhost:8000/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture)
|
||||
.normalize_specifier(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/http/localhost%3A8000/mod.ts").unwrap();
|
||||
|
|
|
@ -280,13 +280,14 @@ fn lsp_import_map_remote() {
|
|||
#[test]
|
||||
fn lsp_import_map_data_url() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let temp_dir = context.temp_dir();
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize(|builder| {
|
||||
builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}");
|
||||
});
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"uri": temp_dir.uri().join("file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import example from \"example\";\n"
|
||||
|
@ -780,7 +781,7 @@ fn lsp_format_vendor_path() {
|
|||
client.initialize_default();
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"uri": temp_dir.uri().join("file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": r#"import "http://localhost:4545/run/002_hello.ts";"#,
|
||||
|
@ -802,7 +803,7 @@ fn lsp_format_vendor_path() {
|
|||
"workspace/executeCommand",
|
||||
json!({
|
||||
"command": "deno.cache",
|
||||
"arguments": [[], "file:///a/file.ts"],
|
||||
"arguments": [[], temp_dir.uri().join("file.ts").unwrap()],
|
||||
}),
|
||||
);
|
||||
assert!(temp_dir
|
||||
|
@ -2622,7 +2623,7 @@ fn lsp_import_map_setting_with_deno_json() {
|
|||
});
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"uri": temp_dir.uri().join("file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"file2\";\n",
|
||||
|
@ -7585,7 +7586,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
client.did_open(
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"uri": temp_dir.uri().join("file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": concat!(
|
||||
|
@ -7612,7 +7613,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
"npm:chalk@~5",
|
||||
"http://localhost:4545/subdir/print_hello.ts",
|
||||
],
|
||||
"file:///a/file.ts",
|
||||
temp_dir.uri().join("file.ts").unwrap(),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
@ -7620,14 +7621,14 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try auto-import with path
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/a.ts",
|
||||
"uri": temp_dir.uri().join("a.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "getClie",
|
||||
}
|
||||
}));
|
||||
let list = client.get_completion_list(
|
||||
"file:///a/a.ts",
|
||||
temp_dir.uri().join("a.ts").unwrap(),
|
||||
(0, 7),
|
||||
json!({ "triggerKind": 1 }),
|
||||
);
|
||||
|
@ -7668,20 +7669,23 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try quick fix with path
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/b.ts",
|
||||
"uri": temp_dir.uri().join("b.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "getClient",
|
||||
}
|
||||
}));
|
||||
let diagnostics = diagnostics
|
||||
.messages_with_file_and_source("file:///a/b.ts", "deno-ts")
|
||||
.messages_with_file_and_source(
|
||||
temp_dir.uri().join("b.ts").unwrap().as_str(),
|
||||
"deno-ts",
|
||||
)
|
||||
.diagnostics;
|
||||
let res = client.write_request(
|
||||
"textDocument/codeAction",
|
||||
json!(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/b.ts"
|
||||
"uri": temp_dir.uri().join("b.ts").unwrap()
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 0 },
|
||||
|
@ -7713,7 +7717,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
"edit": {
|
||||
"documentChanges": [{
|
||||
"textDocument": {
|
||||
"uri": "file:///a/b.ts",
|
||||
"uri": temp_dir.uri().join("b.ts").unwrap(),
|
||||
"version": 1,
|
||||
},
|
||||
"edits": [{
|
||||
|
@ -7731,7 +7735,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try auto-import without path
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/c.ts",
|
||||
"uri": temp_dir.uri().join("c.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "chal",
|
||||
|
@ -7739,7 +7743,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
}));
|
||||
|
||||
let list = client.get_completion_list(
|
||||
"file:///a/c.ts",
|
||||
temp_dir.uri().join("c.ts").unwrap(),
|
||||
(0, 4),
|
||||
json!({ "triggerKind": 1 }),
|
||||
);
|
||||
|
@ -7778,20 +7782,23 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try quick fix without path
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/d.ts",
|
||||
"uri": temp_dir.uri().join("d.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "chalk",
|
||||
}
|
||||
}));
|
||||
let diagnostics = diagnostics
|
||||
.messages_with_file_and_source("file:///a/d.ts", "deno-ts")
|
||||
.messages_with_file_and_source(
|
||||
temp_dir.uri().join("d.ts").unwrap().as_str(),
|
||||
"deno-ts",
|
||||
)
|
||||
.diagnostics;
|
||||
let res = client.write_request(
|
||||
"textDocument/codeAction",
|
||||
json!(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/d.ts"
|
||||
"uri": temp_dir.uri().join("d.ts").unwrap()
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 0 },
|
||||
|
@ -7823,7 +7830,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
"edit": {
|
||||
"documentChanges": [{
|
||||
"textDocument": {
|
||||
"uri": "file:///a/d.ts",
|
||||
"uri": temp_dir.uri().join("d.ts").unwrap(),
|
||||
"version": 1,
|
||||
},
|
||||
"edits": [{
|
||||
|
@ -7841,7 +7848,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try auto-import with http import map
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/e.ts",
|
||||
"uri": temp_dir.uri().join("e.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "printH",
|
||||
|
@ -7849,7 +7856,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
}));
|
||||
|
||||
let list = client.get_completion_list(
|
||||
"file:///a/e.ts",
|
||||
temp_dir.uri().join("e.ts").unwrap(),
|
||||
(0, 6),
|
||||
json!({ "triggerKind": 1 }),
|
||||
);
|
||||
|
@ -7888,20 +7895,23 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try quick fix with http import
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/f.ts",
|
||||
"uri": temp_dir.uri().join("f.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "printHello",
|
||||
}
|
||||
}));
|
||||
let diagnostics = diagnostics
|
||||
.messages_with_file_and_source("file:///a/f.ts", "deno-ts")
|
||||
.messages_with_file_and_source(
|
||||
temp_dir.uri().join("f.ts").unwrap().as_str(),
|
||||
"deno-ts",
|
||||
)
|
||||
.diagnostics;
|
||||
let res = client.write_request(
|
||||
"textDocument/codeAction",
|
||||
json!(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/f.ts"
|
||||
"uri": temp_dir.uri().join("f.ts").unwrap()
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 0 },
|
||||
|
@ -7933,7 +7943,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
"edit": {
|
||||
"documentChanges": [{
|
||||
"textDocument": {
|
||||
"uri": "file:///a/f.ts",
|
||||
"uri": temp_dir.uri().join("f.ts").unwrap(),
|
||||
"version": 1,
|
||||
},
|
||||
"edits": [{
|
||||
|
@ -7951,14 +7961,14 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
// try auto-import with npm package with sub-path on value side of import map
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/nested_path.ts",
|
||||
"uri": temp_dir.uri().join("nested_path.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "entry",
|
||||
}
|
||||
}));
|
||||
let list = client.get_completion_list(
|
||||
"file:///a/nested_path.ts",
|
||||
temp_dir.uri().join("nested_path.ts").unwrap(),
|
||||
(0, 5),
|
||||
json!({ "triggerKind": 1 }),
|
||||
);
|
||||
|
@ -11001,7 +11011,7 @@ fn lsp_lint_with_config() {
|
|||
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"uri": temp_dir.uri().join("file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "// TODO: fixme\nexport async function non_camel_case() {\nconsole.log(\"finished!\")\n}"
|
||||
|
@ -12104,6 +12114,323 @@ fn lsp_vendor_dir() {
|
|||
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_deno_json_scopes_import_map() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let temp_dir = context.temp_dir();
|
||||
temp_dir.create_dir_all("project1");
|
||||
temp_dir.create_dir_all("project2/project3");
|
||||
temp_dir.write(
|
||||
"project1/deno.json",
|
||||
json!({
|
||||
"imports": {
|
||||
"foo": "./foo1.ts",
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.write("project1/foo1.ts", "");
|
||||
temp_dir.write(
|
||||
"project2/deno.json",
|
||||
json!({
|
||||
"imports": {
|
||||
"foo": "./foo2.ts",
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.write("project2/foo2.ts", "");
|
||||
temp_dir.write(
|
||||
"project2/project3/deno.json",
|
||||
json!({
|
||||
"imports": {
|
||||
"foo": "./foo3.ts",
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.write("project2/project3/foo3.ts", "");
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"foo\";\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/hover",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": format!("**Resolved Dependency**\n\n**Code**: file​{}\n", temp_dir.uri().join("project1/foo1.ts").unwrap().as_str().trim_start_matches("file")),
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 7 },
|
||||
"end": { "line": 0, "character": 12 },
|
||||
},
|
||||
})
|
||||
);
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"foo\";\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/hover",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": format!("**Resolved Dependency**\n\n**Code**: file​{}\n", temp_dir.uri().join("project2/foo2.ts").unwrap().as_str().trim_start_matches("file")),
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 7 },
|
||||
"end": { "line": 0, "character": 12 },
|
||||
},
|
||||
})
|
||||
);
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/project3/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"foo\";\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/hover",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/project3/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": format!("**Resolved Dependency**\n\n**Code**: file​{}\n", temp_dir.uri().join("project2/project3/foo3.ts").unwrap().as_str().trim_start_matches("file")),
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 7 },
|
||||
"end": { "line": 0, "character": 12 },
|
||||
},
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_deno_json_scopes_vendor_dirs() {
|
||||
let context = TestContextBuilder::new()
|
||||
.use_http_server()
|
||||
.use_temp_cwd()
|
||||
.build();
|
||||
let temp_dir = context.temp_dir();
|
||||
temp_dir.create_dir_all("project1");
|
||||
temp_dir.create_dir_all("project2/project3");
|
||||
temp_dir.write(
|
||||
"project1/deno.json",
|
||||
json!({
|
||||
"vendor": true,
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.write(
|
||||
"project2/deno.json",
|
||||
json!({
|
||||
"vendor": true,
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.write(
|
||||
"project2/project3/deno.json",
|
||||
json!({
|
||||
"vendor": true,
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"http://localhost:4545/subdir/mod1.ts\";\n",
|
||||
},
|
||||
}));
|
||||
client.write_request(
|
||||
"workspace/executeCommand",
|
||||
json!({
|
||||
"command": "deno.cache",
|
||||
"arguments": [[], temp_dir.uri().join("project1/file.ts").unwrap()],
|
||||
}),
|
||||
);
|
||||
let res = client.write_request(
|
||||
"textDocument/definition",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([{
|
||||
"targetUri": temp_dir.uri().join("project1/vendor/http_localhost_4545/subdir/mod1.ts").unwrap(),
|
||||
"targetRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 17,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
"targetSelectionRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 17,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
}]),
|
||||
);
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"http://localhost:4545/subdir/mod2.ts\";\n",
|
||||
},
|
||||
}));
|
||||
client.write_request(
|
||||
"workspace/executeCommand",
|
||||
json!({
|
||||
"command": "deno.cache",
|
||||
"arguments": [[], temp_dir.uri().join("project2/file.ts").unwrap()],
|
||||
}),
|
||||
);
|
||||
let res = client.write_request(
|
||||
"textDocument/definition",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([{
|
||||
"targetUri": temp_dir.uri().join("project2/vendor/http_localhost_4545/subdir/mod2.ts").unwrap(),
|
||||
"targetRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
"targetSelectionRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
}]),
|
||||
);
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/project3/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import \"http://localhost:4545/subdir/mod3.js\";\n",
|
||||
},
|
||||
}));
|
||||
client.write_request(
|
||||
"workspace/executeCommand",
|
||||
json!({
|
||||
"command": "deno.cache",
|
||||
"arguments": [[], temp_dir.uri().join("project2/project3/file.ts").unwrap()],
|
||||
}),
|
||||
);
|
||||
let res = client.write_request(
|
||||
"textDocument/definition",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/project3/file.ts").unwrap(),
|
||||
},
|
||||
"position": { "line": 0, "character": 7 },
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([{
|
||||
"targetUri": temp_dir.uri().join("project2/project3/vendor/http_localhost_4545/subdir/mod3.js").unwrap(),
|
||||
"targetRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
"targetSelectionRange": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 0,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 0,
|
||||
},
|
||||
},
|
||||
}]),
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_deno_json_workspace_fmt_config() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
|
@ -13005,7 +13332,7 @@ fn lsp_uses_lockfile_for_npm_initialization() {
|
|||
assert!(!line.contains("Running npm resolution."), "Line: {}", line);
|
||||
line.contains("Server ready.")
|
||||
});
|
||||
assert_eq!(skipping_count, 1);
|
||||
assert_eq!(skipping_count, 2);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue