Skip to content

Commit

Permalink
feat(unstable/lsp): support navigating to deno_modules folder (#20030)
Browse files Browse the repository at this point in the history
Closes #20015
Closes denoland/vscode_deno#850 (only for
deno_modules, but I don't think this will be possible for the global
cache)
  • Loading branch information
dsherret authored Aug 2, 2023
1 parent e8d0311 commit 480894e
Show file tree
Hide file tree
Showing 11 changed files with 586 additions and 80 deletions.
424 changes: 376 additions & 48 deletions cli/cache/http_cache/local.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cli/cache/http_cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod local;
pub use global::url_to_filename;
pub use global::GlobalHttpCache;
pub use local::LocalHttpCache;
pub use local::LocalLspHttpCache;

/// Cached metadata about a url.
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
Expand Down
1 change: 1 addition & 0 deletions cli/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub use http_cache::CachedUrlMetadata;
pub use http_cache::GlobalHttpCache;
pub use http_cache::HttpCache;
pub use http_cache::LocalHttpCache;
pub use http_cache::LocalLspHttpCache;
pub use incremental::IncrementalCache;
pub use node::NodeAnalysisCache;
pub use parsed_source::ParsedSourceCache;
Expand Down
7 changes: 5 additions & 2 deletions cli/lsp/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,14 @@ impl OutsideLockClient {

pub async fn publish_diagnostics(
&self,
uri: lsp::Url,
uri: LspClientUrl,
diags: Vec<lsp::Diagnostic>,
version: Option<i32>,
) {
self.0.publish_diagnostics(uri, diags, version).await;
self
.0
.publish_diagnostics(uri.into_url(), diags, version)
.await;
}
}

Expand Down
38 changes: 33 additions & 5 deletions cli/lsp/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use super::language_server::StateSnapshot;
use super::performance::Performance;
use super::tsc;
use super::tsc::TsServer;
use super::urls::LspClientUrl;
use super::urls::LspUrlMap;

use crate::args::LintOptions;
use crate::graph_util;
Expand Down Expand Up @@ -54,6 +56,7 @@ pub struct DiagnosticServerUpdateMessage {
pub snapshot: Arc<StateSnapshot>,
pub config: Arc<ConfigSnapshot>,
pub lint_options: LintOptions,
pub url_map: LspUrlMap,
}

struct DiagnosticRecord {
Expand Down Expand Up @@ -107,6 +110,7 @@ impl DiagnosticsPublisher {
&self,
source: DiagnosticSource,
diagnostics: DiagnosticVec,
url_map: &LspUrlMap,
token: &CancellationToken,
) -> usize {
let mut diagnostics_by_specifier =
Expand Down Expand Up @@ -141,7 +145,9 @@ impl DiagnosticsPublisher {
.client
.when_outside_lsp_lock()
.publish_diagnostics(
record.specifier,
url_map
.normalize_specifier(&record.specifier)
.unwrap_or(LspClientUrl::new(record.specifier)),
all_specifier_diagnostics,
version,
)
Expand Down Expand Up @@ -169,7 +175,9 @@ impl DiagnosticsPublisher {
.client
.when_outside_lsp_lock()
.publish_diagnostics(
specifier.clone(),
url_map
.normalize_specifier(specifier)
.unwrap_or_else(|_| LspClientUrl::new(specifier.clone())),
Vec::new(),
removed_value.version,
)
Expand Down Expand Up @@ -366,9 +374,11 @@ impl DiagnosticsServer {
snapshot,
config,
lint_options,
url_map,
},
batch_index,
} = message;
let url_map = Arc::new(url_map);

// cancel the previous run
token.cancel();
Expand All @@ -383,6 +393,7 @@ impl DiagnosticsServer {
let ts_diagnostics_store = ts_diagnostics_store.clone();
let snapshot = snapshot.clone();
let config = config.clone();
let url_map = url_map.clone();
async move {
if let Some(previous_handle) = previous_ts_handle {
// Wait on the previous run to complete in order to prevent
Expand Down Expand Up @@ -419,7 +430,12 @@ impl DiagnosticsServer {
if !token.is_cancelled() {
ts_diagnostics_store.update(&diagnostics);
messages_len = diagnostics_publisher
.publish(DiagnosticSource::Ts, diagnostics, &token)
.publish(
DiagnosticSource::Ts,
diagnostics,
&url_map,
&token,
)
.await;

if !token.is_cancelled() {
Expand Down Expand Up @@ -447,6 +463,7 @@ impl DiagnosticsServer {
let token = token.clone();
let snapshot = snapshot.clone();
let config = config.clone();
let url_map = url_map.clone();
async move {
if let Some(previous_handle) = previous_deps_handle {
previous_handle.await;
Expand All @@ -463,7 +480,12 @@ impl DiagnosticsServer {
let mut messages_len = 0;
if !token.is_cancelled() {
messages_len = diagnostics_publisher
.publish(DiagnosticSource::Deno, diagnostics, &token)
.publish(
DiagnosticSource::Deno,
diagnostics,
&url_map,
&token,
)
.await;

if !token.is_cancelled() {
Expand Down Expand Up @@ -491,6 +513,7 @@ impl DiagnosticsServer {
let token = token.clone();
let snapshot = snapshot.clone();
let config = config.clone();
let url_map = url_map.clone();
async move {
if let Some(previous_handle) = previous_lint_handle {
previous_handle.await;
Expand All @@ -514,7 +537,12 @@ impl DiagnosticsServer {
let mut messages_len = 0;
if !token.is_cancelled() {
messages_len = diagnostics_publisher
.publish(DiagnosticSource::Lint, diagnostics, &token)
.publish(
DiagnosticSource::Lint,
diagnostics,
&url_map,
&token,
)
.await;

if !token.is_cancelled() {
Expand Down
9 changes: 8 additions & 1 deletion cli/lsp/documents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,13 @@ impl Documents {
}
}

pub fn resolve_redirected(
&self,
specifier: &ModuleSpecifier,
) -> Option<ModuleSpecifier> {
self.specifier_resolver.resolve(specifier)
}

/// Return `true` if the specifier can be resolved to a document.
pub fn exists(&self, specifier: &ModuleSpecifier) -> bool {
let specifier = self.specifier_resolver.resolve(specifier);
Expand Down Expand Up @@ -1498,7 +1505,7 @@ impl Documents {
self.resolve_dependency(specifier, maybe_node_resolver)
} else {
let media_type = doc.media_type();
Some((specifier.clone(), media_type))
Some((doc.specifier().clone(), media_type))
}
}

Expand Down
21 changes: 12 additions & 9 deletions cli/lsp/language_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ use crate::cache::DenoDir;
use crate::cache::FastInsecureHasher;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::cache::LocalHttpCache;
use crate::cache::LocalLspHttpCache;
use crate::factory::CliFactory;
use crate::file_fetcher::FileFetcher;
use crate::graph_util;
Expand Down Expand Up @@ -204,7 +204,7 @@ pub struct Inner {
/// An abstraction that handles interactions with TypeScript.
pub ts_server: Arc<TsServer>,
/// A map of specifiers and URLs used to translate over the LSP.
pub url_map: Arc<urls::LspUrlMap>,
pub url_map: urls::LspUrlMap,
}

impl LanguageServer {
Expand Down Expand Up @@ -905,16 +905,18 @@ impl Inner {
self.module_registries_location = module_registries_location;
// update the cache path
let global_cache = Arc::new(GlobalHttpCache::new(dir.deps_folder_path()));
let cache: Arc<dyn HttpCache> =
match self.config.maybe_deno_modules_dir_path() {
Some(local_path) => {
Arc::new(LocalHttpCache::new(local_path, global_cache))
}
None => global_cache,
};
let maybe_local_cache =
self.config.maybe_deno_modules_dir_path().map(|local_path| {
Arc::new(LocalLspHttpCache::new(local_path, global_cache.clone()))
});
let cache: Arc<dyn HttpCache> = maybe_local_cache
.clone()
.map(|c| c as Arc<dyn HttpCache>)
.unwrap_or(global_cache);
self.deps_http_cache = cache.clone();
self.documents.set_cache(cache.clone());
self.cache_metadata.set_cache(cache);
self.url_map.set_cache(maybe_local_cache);
self.maybe_global_cache_path = new_cache_path;
Ok(())
}
Expand Down Expand Up @@ -2946,6 +2948,7 @@ impl Inner {
snapshot: self.snapshot(),
config: self.config.snapshot(),
lint_options: self.lint_options.clone(),
url_map: self.url_map.clone(),
};
if let Err(err) = self.diagnostics_server.update(snapshot) {
error!("Cannot update diagnostics: {}", err);
Expand Down
10 changes: 7 additions & 3 deletions cli/lsp/tsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3214,9 +3214,13 @@ fn op_script_names(state: &mut OpState) -> Vec<String> {
.filter_map(|dep| dep.get_type().or_else(|| dep.get_code())),
);
for specifier in specifiers {
if seen.insert(specifier.as_str()) && documents.exists(specifier) {
// only include dependencies we know to exist otherwise typescript will error
result.push(specifier.to_string());
if seen.insert(specifier.as_str()) {
if let Some(specifier) = documents.resolve_redirected(specifier) {
// only include dependencies we know to exist otherwise typescript will error
if documents.exists(&specifier) {
result.push(specifier.to_string());
}
}
}
}
}
Expand Down
33 changes: 29 additions & 4 deletions cli/lsp/urls.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.

use crate::cache::LocalLspHttpCache;
use crate::file_fetcher::map_content_type;

use data_url::DataUrl;
Expand All @@ -12,6 +13,7 @@ use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Arc;

/// Used in situations where a default URL needs to be used where otherwise a
/// panic is undesired.
Expand Down Expand Up @@ -119,17 +121,31 @@ pub enum LspUrlKind {
/// A bi-directional map of URLs sent to the LSP client and internal module
/// specifiers. We need to map internal specifiers into `deno:` schema URLs
/// to allow the Deno language server to manage these as virtual documents.
#[derive(Debug, Default)]
pub struct LspUrlMap(Mutex<LspUrlMapInner>);
#[derive(Debug, Default, Clone)]
pub struct LspUrlMap {
local_http_cache: Option<Arc<LocalLspHttpCache>>,
inner: Arc<Mutex<LspUrlMapInner>>,
}

impl LspUrlMap {
pub fn set_cache(&mut self, http_cache: Option<Arc<LocalLspHttpCache>>) {
self.local_http_cache = http_cache;
}

/// Normalize a specifier that is used internally within Deno (or tsc) to a
/// URL that can be handled as a "virtual" document by an LSP client.
pub fn normalize_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Result<LspClientUrl, AnyError> {
let mut inner = self.0.lock();
if let Some(cache) = &self.local_http_cache {
if matches!(specifier.scheme(), "http" | "https") {
if let Some(file_url) = cache.get_file_url(specifier) {
return Ok(LspClientUrl(file_url));
}
}
}
let mut inner = self.inner.lock();
if let Some(url) = inner.get_url(specifier).cloned() {
Ok(url)
} else {
Expand Down Expand Up @@ -183,7 +199,16 @@ 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 {
let mut inner = self.0.lock();
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) {
return remote_url;
}
}
}
}
let mut inner = self.inner.lock();
if let Some(specifier) = inner.get_specifier(url).cloned() {
specifier
} else {
Expand Down
Loading

0 comments on commit 480894e

Please sign in to comment.