Skip to content

Commit

Permalink
refactor(lsp): unify caching into LspCache (#23746)
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn authored and dsherret committed May 10, 2024
1 parent cc7ae84 commit f7f9383
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 494 deletions.
5 changes: 2 additions & 3 deletions cli/cache/deno_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ impl DenoDirProvider {

/// `DenoDir` serves as coordinator for multiple `DiskCache`s containing them
/// in single directory that can be controlled with `$DENO_DIR` env variable.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct DenoDir {
/// Example: /Users/rld/.deno/
/// Note: This is not exposed in order to encourage using re-usable methods.
root: PathBuf,
pub root: PathBuf,
/// Used by TsCompiler to cache compiler output.
pub gen_cache: DiskCache,
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cache/disk_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::path::PathBuf;
use std::path::Prefix;
use std::str;

#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct DiskCache {
pub location: PathBuf,
}
Expand Down
136 changes: 65 additions & 71 deletions cli/lsp/cache.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use crate::cache::DenoDir;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::cache::LocalLspHttpCache;
use crate::lsp::config::Config;
use crate::lsp::logging::lsp_log;
use crate::lsp::logging::lsp_warn;
use deno_runtime::fs_util::specifier_to_file_path;

use deno_core::parking_lot::Mutex;
use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::sync::Arc;
Expand All @@ -22,7 +27,7 @@ pub const LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY: deno_cache_dir::GlobalToLocalCopy =
deno_cache_dir::GlobalToLocalCopy::Disallow;

pub fn calculate_fs_version(
cache: &Arc<dyn HttpCache>,
cache: &LspCache,
specifier: &ModuleSpecifier,
) -> Option<String> {
match specifier.scheme() {
Expand All @@ -49,13 +54,14 @@ pub fn calculate_fs_version_at_path(path: &Path) -> Option<String> {
}

fn calculate_fs_version_in_cache(
cache: &Arc<dyn HttpCache>,
cache: &LspCache,
specifier: &ModuleSpecifier,
) -> Option<String> {
let Ok(cache_key) = cache.cache_item_key(specifier) else {
let http_cache = cache.root_vendor_or_global();
let Ok(cache_key) = http_cache.cache_item_key(specifier) else {
return Some("1".to_string());
};
match cache.read_modified_time(&cache_key) {
match http_cache.read_modified_time(&cache_key) {
Ok(Some(modified)) => {
match modified.duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => Some(n.as_millis().to_string()),
Expand All @@ -67,83 +73,71 @@ fn calculate_fs_version_in_cache(
}
}

/// Populate the metadata map based on the supplied headers
fn parse_metadata(
headers: &HashMap<String, String>,
) -> HashMap<MetadataKey, String> {
let mut metadata = HashMap::new();
if let Some(warning) = headers.get("x-deno-warning").cloned() {
metadata.insert(MetadataKey::Warning, warning);
}
metadata
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub enum MetadataKey {
/// Represent the `x-deno-warning` header associated with the document
Warning,
}

#[derive(Debug, Clone)]
struct Metadata {
values: Arc<HashMap<MetadataKey, String>>,
version: Option<String>,
pub struct LspCache {
deno_dir: DenoDir,
global: Arc<GlobalHttpCache>,
root_vendor: Option<Arc<LocalLspHttpCache>>,
}

#[derive(Debug, Clone)]
pub struct CacheMetadata {
cache: Arc<dyn HttpCache>,
metadata: Arc<Mutex<HashMap<ModuleSpecifier, Metadata>>>,
impl Default for LspCache {
fn default() -> Self {
Self::new(None)
}
}

impl CacheMetadata {
pub fn new(cache: Arc<dyn HttpCache>) -> Self {
impl LspCache {
pub fn new(global_cache_url: Option<Url>) -> Self {
let global_cache_path = global_cache_url.and_then(|s| {
specifier_to_file_path(&s)
.inspect(|p| {
lsp_log!("Resolved global cache path: \"{}\"", p.to_string_lossy());
})
.inspect_err(|err| {
lsp_warn!("Failed to resolve custom cache path: {err}");
})
.ok()
});
let deno_dir = DenoDir::new(global_cache_path)
.expect("should be infallible with absolute custom root");
let global = Arc::new(GlobalHttpCache::new(
deno_dir.deps_folder_path(),
crate::cache::RealDenoCacheEnv,
));
Self {
cache,
metadata: Default::default(),
deno_dir,
global,
root_vendor: None,
}
}

/// Return the meta data associated with the specifier. Unlike the `get()`
/// method, redirects of the supplied specifier will not be followed.
pub fn get(
&self,
specifier: &ModuleSpecifier,
) -> Option<Arc<HashMap<MetadataKey, String>>> {
if matches!(
specifier.scheme(),
"file" | "npm" | "node" | "data" | "blob"
) {
return None;
}
let version = calculate_fs_version_in_cache(&self.cache, specifier);
let metadata = self.metadata.lock().get(specifier).cloned();
if metadata.as_ref().and_then(|m| m.version.clone()) != version {
self.refresh(specifier).map(|m| m.values)
} else {
metadata.map(|m| m.values)
}
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(),
)))
});
}

fn refresh(&self, specifier: &ModuleSpecifier) -> Option<Metadata> {
if matches!(
specifier.scheme(),
"file" | "npm" | "node" | "data" | "blob"
) {
return None;
}
let cache_key = self.cache.cache_item_key(specifier).ok()?;
let headers = self.cache.read_headers(&cache_key).ok()??;
let values = Arc::new(parse_metadata(&headers));
let version = calculate_fs_version_in_cache(&self.cache, specifier);
let mut metadata_map = self.metadata.lock();
let metadata = Metadata { values, version };
metadata_map.insert(specifier.clone(), metadata.clone());
Some(metadata)
pub fn deno_dir(&self) -> &DenoDir {
&self.deno_dir
}

pub fn global(&self) -> &Arc<GlobalHttpCache> {
&self.global
}

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

pub fn set_cache(&mut self, cache: Arc<dyn HttpCache>) {
self.cache = cache;
self.metadata.lock().clear();
pub fn root_vendor_or_global(&self) -> Arc<dyn HttpCache> {
self
.root_vendor
.as_ref()
.map(|v| v.clone() as _)
.unwrap_or(self.global.clone() as _)
}
}
47 changes: 17 additions & 30 deletions cli/lsp/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,41 +799,39 @@ fn get_workspace_completions(
#[cfg(test)]
mod tests {
use super::*;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::lsp::cache::LspCache;
use crate::lsp::documents::Documents;
use crate::lsp::documents::LanguageId;
use crate::lsp::search::tests::TestPackageSearchApi;
use deno_core::resolve_url;
use deno_graph::Range;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use test_util::TempDir;

fn mock_documents(
fixtures: &[(&str, &str, i32, LanguageId)],
source_fixtures: &[(&str, &str)],
location: &Path,
fn setup(
open_sources: &[(&str, &str, i32, LanguageId)],
fs_sources: &[(&str, &str)],
) -> Documents {
let cache = Arc::new(GlobalHttpCache::new(
location.to_path_buf(),
crate::cache::RealDenoCacheEnv,
));
let mut documents = Documents::new(cache);
for (specifier, source, version, language_id) in fixtures {
let temp_dir = TempDir::new();
let cache = LspCache::new(Some(temp_dir.uri()));
let mut documents = Documents::default();
documents.update_config(
&Default::default(),
&Default::default(),
&cache,
&Default::default(),
);
for (specifier, source, version, language_id) in open_sources {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
documents.open(specifier, *version, *language_id, (*source).into());
}
let http_cache = GlobalHttpCache::new(
location.to_path_buf(),
crate::cache::RealDenoCacheEnv,
);
for (specifier, source) in source_fixtures {
for (specifier, source) in fs_sources {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
http_cache
cache
.global()
.set(&specifier, HashMap::default(), source.as_bytes())
.expect("could not cache file");
assert!(
Expand All @@ -844,15 +842,6 @@ mod tests {
documents
}

fn setup(
temp_dir: &TempDir,
documents: &[(&str, &str, i32, LanguageId)],
sources: &[(&str, &str)],
) -> Documents {
let location = temp_dir.path().join("deps");
mock_documents(documents, sources, location.as_path())
}

#[test]
fn test_get_relative_specifiers() {
let base = resolve_url("file:///a/b/c.ts").unwrap();
Expand Down Expand Up @@ -936,9 +925,7 @@ mod tests {
character: 21,
},
};
let temp_dir = TempDir::new();
let documents = setup(
&temp_dir,
&[
(
"file:///a/b/c.ts",
Expand Down
4 changes: 0 additions & 4 deletions cli/lsp/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,10 +1454,6 @@ impl ConfigTree {
.unwrap_or_default()
}

pub fn root_vendor_dir(&self) -> Option<&PathBuf> {
self.root_data().and_then(|d| d.vendor_dir.as_ref())
}

pub fn root_lockfile(&self) -> Option<&Arc<Mutex<Lockfile>>> {
self.root_data().and_then(|d| d.lockfile.as_ref())
}
Expand Down
Loading

0 comments on commit f7f9383

Please sign in to comment.