summaryrefslogtreecommitdiff
path: root/cli/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp')
-rw-r--r--cli/lsp/diagnostics.rs10
-rw-r--r--cli/lsp/documents.rs6
-rw-r--r--cli/lsp/language_server.rs444
3 files changed, 344 insertions, 116 deletions
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 7b5a30a0e..1a57ad03b 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -908,10 +908,7 @@ fn diagnose_resolution(
{
if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
// show diagnostics for npm package references that aren't cached
- if npm_resolver
- .resolve_pkg_id_from_pkg_req(&pkg_ref.req)
- .is_err()
- {
+ if !npm_resolver.is_pkg_req_folder_cached(&pkg_ref.req) {
diagnostics
.push(DenoDiagnostic::NoCacheNpm(pkg_ref, specifier.clone()));
}
@@ -925,10 +922,7 @@ fn diagnose_resolution(
// check that a @types/node package exists in the resolver
let types_node_ref =
NpmPackageReqReference::from_str("npm:@types/node").unwrap();
- if npm_resolver
- .resolve_pkg_id_from_pkg_req(&types_node_ref.req)
- .is_err()
- {
+ if !npm_resolver.is_pkg_req_folder_cached(&types_node_ref.req) {
diagnostics.push(DenoDiagnostic::NoCacheNpm(
types_node_ref,
ModuleSpecifier::parse("npm:@types/node").unwrap(),
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index 8fd9cdbb2..4bfb9342a 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -1224,11 +1224,7 @@ impl Documents {
);
let deps_provider =
Arc::new(PackageJsonDepsProvider::new(maybe_package_json_deps));
- let deps_installer = Arc::new(PackageJsonDepsInstaller::new(
- deps_provider.clone(),
- options.npm_registry_api.clone(),
- options.npm_resolution.clone(),
- ));
+ let deps_installer = Arc::new(PackageJsonDepsInstaller::no_op());
self.resolver = Arc::new(CliGraphResolver::new(
maybe_jsx_config,
options.maybe_import_map,
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 25d908a52..594c4adbc 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -4,12 +4,15 @@ use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
+use deno_core::parking_lot::Mutex;
use deno_core::resolve_url;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::task::spawn;
use deno_core::ModuleSpecifier;
+use deno_lockfile::Lockfile;
+use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver;
@@ -71,6 +74,7 @@ use super::urls::LspClientUrl;
use crate::args::get_root_cert_store;
use crate::args::package_json;
use crate::args::resolve_import_map_from_specifier;
+use crate::args::snapshot_from_lockfile;
use crate::args::CaData;
use crate::args::CacheSetting;
use crate::args::CliOptions;
@@ -80,6 +84,7 @@ use crate::args::FmtOptions;
use crate::args::LintOptions;
use crate::args::TsConfig;
use crate::cache::DenoDir;
+use crate::cache::FastInsecureHasher;
use crate::cache::HttpCache;
use crate::factory::CliFactory;
use crate::file_fetcher::FileFetcher;
@@ -93,6 +98,7 @@ use crate::npm::NpmCache;
use crate::npm::NpmResolution;
use crate::tools::fmt::format_file;
use crate::tools::fmt::format_parsed_source;
+use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::fs::remove_dir_all_if_exists;
use crate::util::path::specifier_to_file_path;
use crate::util::progress_bar::ProgressBar;
@@ -106,6 +112,71 @@ impl RootCertStoreProvider for LspRootCertStoreProvider {
}
}
+#[derive(Debug)]
+struct LspNpmServices {
+ /// When this hash changes, the services need updating
+ config_hash: LspNpmConfigHash,
+ /// Npm's registry api.
+ api: Arc<CliNpmRegistryApi>,
+ /// Npm cache
+ cache: Arc<NpmCache>,
+ /// Npm resolution that is stored in memory.
+ resolution: Arc<NpmResolution>,
+ /// Resolver for npm packages.
+ resolver: Arc<CliNpmResolver>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+struct LspNpmConfigHash(u64);
+
+impl LspNpmConfigHash {
+ pub fn from_inner(inner: &Inner) -> Self {
+ let mut hasher = FastInsecureHasher::new();
+ hasher.write_hashable(&inner.maybe_node_modules_dir_path());
+ hasher.write_hashable(&inner.maybe_cache_path);
+ hash_lockfile_with_hasher(&mut hasher, inner.maybe_lockfile());
+ Self(hasher.finish())
+ }
+}
+
+fn hash_lockfile(maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>) -> u64 {
+ let mut hasher = FastInsecureHasher::new();
+ hash_lockfile_with_hasher(&mut hasher, maybe_lockfile);
+ hasher.finish()
+}
+
+fn hash_lockfile_with_hasher(
+ hasher: &mut FastInsecureHasher,
+ maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>,
+) {
+ if let Some(lockfile) = &maybe_lockfile {
+ let lockfile = lockfile.lock();
+ hasher.write_hashable(&*lockfile);
+ }
+}
+
+fn resolve_lockfile_from_path(
+ lockfile_path: PathBuf,
+) -> Option<Arc<Mutex<Lockfile>>> {
+ match Lockfile::new(lockfile_path, false) {
+ Ok(value) => Some(Arc::new(Mutex::new(value))),
+ Err(err) => {
+ lsp_warn!("Error loading lockfile: {:#}", err);
+ None
+ }
+ }
+}
+
+/// Contains the config file and dependent information.
+#[derive(Debug)]
+struct LspConfigFileInfo {
+ config_file: ConfigFile,
+ /// An optional deno.lock file, which is resolved relative to the config file.
+ maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
+ /// The canonicalized node_modules directory, which is found relative to the config file.
+ maybe_node_modules_dir: Option<PathBuf>,
+}
+
#[derive(Debug, Clone)]
pub struct LanguageServer(Arc<tokio::sync::RwLock<Inner>>);
@@ -146,8 +217,8 @@ pub struct Inner {
/// options.
maybe_cache_path: Option<PathBuf>,
/// An optional configuration file which has been specified in the client
- /// options.
- maybe_config_file: Option<ConfigFile>,
+ /// options along with some data that is computed after the config file is set.
+ maybe_config_file_info: Option<LspConfigFileInfo>,
/// An optional import map which is used to resolve modules.
maybe_import_map: Option<Arc<ImportMap>>,
/// The URL for the import map which is used to determine relative imports.
@@ -160,14 +231,8 @@ pub struct Inner {
lint_options: LintOptions,
/// A lazily create "server" for handling test run requests.
maybe_testing_server: Option<testing::TestServer>,
- /// Npm's registry api.
- npm_api: Arc<CliNpmRegistryApi>,
- /// Npm cache
- npm_cache: Arc<NpmCache>,
- /// Npm resolution that is stored in memory.
- npm_resolution: Arc<NpmResolution>,
- /// Resolver for npm packages.
- npm_resolver: Arc<CliNpmResolver>,
+ /// Services used for dealing with npm related functionality.
+ npm: LspNpmServices,
/// A collection of measurements which instrument that performance of the LSP.
performance: Arc<Performance>,
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
@@ -198,7 +263,8 @@ impl LanguageServer {
.into_iter()
.map(|d| (d.specifier().clone(), d))
.collect::<HashMap<_, _>>();
- let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = Arc::new(cli_options);
+ let factory = CliFactory::from_cli_options(cli_options.clone());
let module_graph_builder = factory.module_graph_builder().await?;
let mut inner_loader = module_graph_builder.create_graph_loader();
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
@@ -217,13 +283,23 @@ impl LanguageServer {
check_js: false,
},
)?;
+
+ // Update the lockfile on the file system with anything new
+ // found after caching
+ if let Some(lockfile) = cli_options.maybe_lockfile() {
+ let lockfile = lockfile.lock();
+ if let Err(err) = lockfile.write() {
+ lsp_warn!("Error writing lockfile: {}", err);
+ }
+ }
+
Ok(())
}
match params.map(serde_json::from_value) {
Some(Ok(params)) => {
// do as much as possible in a read, then do a write outside
- let maybe_cache_result = {
+ let maybe_prepare_cache_result = {
let inner = self.0.read().await; // ensure dropped
match inner.prepare_cache(params) {
Ok(maybe_cache_result) => maybe_cache_result,
@@ -238,7 +314,7 @@ impl LanguageServer {
}
}
};
- if let Some(result) = maybe_cache_result {
+ if let Some(result) = maybe_prepare_cache_result {
let cli_options = result.cli_options;
let roots = result.roots;
let open_docs = result.open_docs;
@@ -420,28 +496,33 @@ impl LanguageServer {
if ls.config.update_enabled_paths() {
touched = true;
}
-
- if touched {
- ls.refresh_documents_config();
- ls.diagnostics_server.invalidate_all();
- ls.send_diagnostics_update();
- }
}
touched
}
}
-fn create_lsp_structs(
+fn resolve_node_modules_dir(config_file: &ConfigFile) -> Option<PathBuf> {
+ // For the language server, require an explicit opt-in via the
+ // `nodeModulesDir: true` setting in the deno.json file. This is to
+ // reduce the chance of modifying someone's node_modules directory
+ // without them having asked us to do so.
+ if config_file.node_modules_dir() != Some(true) {
+ return None;
+ }
+ if config_file.specifier.scheme() != "file" {
+ return None;
+ }
+ let file_path = config_file.specifier.to_file_path().ok()?;
+ let node_modules_dir = file_path.parent()?.join("node_modules");
+ canonicalize_path_maybe_not_exists(&node_modules_dir).ok()
+}
+
+fn create_npm_api_and_cache(
dir: &DenoDir,
http_client: Arc<HttpClient>,
-) -> (
- Arc<CliNpmRegistryApi>,
- Arc<NpmCache>,
- Arc<CliNpmResolver>,
- Arc<NpmResolution>,
-) {
- let registry_url = CliNpmRegistryApi::default_url();
- let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
+ registry_url: &ModuleSpecifier,
+ progress_bar: &ProgressBar,
+) -> (Arc<CliNpmRegistryApi>, Arc<NpmCache>) {
let npm_cache = Arc::new(NpmCache::new(
dir.npm_folder_path(),
// Use an "only" cache setting in order to make the
@@ -458,25 +539,41 @@ fn create_lsp_structs(
http_client,
progress_bar.clone(),
));
- let resolution =
- Arc::new(NpmResolution::from_serialized(api.clone(), None, None));
+ (api, npm_cache)
+}
+
+fn create_npm_resolver_and_resolution(
+ registry_url: &ModuleSpecifier,
+ progress_bar: ProgressBar,
+ api: Arc<CliNpmRegistryApi>,
+ npm_cache: Arc<NpmCache>,
+ node_modules_dir_path: Option<PathBuf>,
+ maybe_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
+) -> (Arc<CliNpmResolver>, Arc<NpmResolution>) {
+ let resolution = Arc::new(NpmResolution::from_serialized(
+ api,
+ maybe_snapshot,
+ // Don't provide the lockfile. We don't want these resolvers
+ // updating it. Only the cache request should update the lockfile.
+ None,
+ ));
let fs = Arc::new(deno_fs::RealFs);
let fs_resolver = create_npm_fs_resolver(
fs.clone(),
- npm_cache.clone(),
+ npm_cache,
&progress_bar,
registry_url.clone(),
resolution.clone(),
- None,
+ node_modules_dir_path,
NpmSystemInfo::default(),
);
(
- api,
- npm_cache,
Arc::new(CliNpmResolver::new(
fs,
resolution.clone(),
fs_resolver,
+ // Don't provide the lockfile. We don't want these resolvers
+ // updating it. Only the cache request should update the lockfile.
None,
)),
resolution,
@@ -505,8 +602,23 @@ impl Inner {
ts_server.clone(),
);
let assets = Assets::new(ts_server.clone());
- let (npm_api, npm_cache, npm_resolver, npm_resolution) =
- create_lsp_structs(&dir, http_client.clone());
+ let registry_url = CliNpmRegistryApi::default_url();
+ let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
+
+ let (npm_api, npm_cache) = create_npm_api_and_cache(
+ &dir,
+ http_client.clone(),
+ registry_url,
+ &progress_bar,
+ );
+ let (npm_resolver, npm_resolution) = create_npm_resolver_and_resolution(
+ registry_url,
+ progress_bar,
+ npm_api.clone(),
+ npm_cache.clone(),
+ None,
+ None,
+ );
Self {
assets,
@@ -518,7 +630,7 @@ impl Inner {
documents,
http_client,
maybe_cache_path: None,
- maybe_config_file: None,
+ maybe_config_file_info: None,
maybe_import_map: None,
maybe_import_map_uri: None,
maybe_package_json: None,
@@ -527,10 +639,13 @@ impl Inner {
maybe_testing_server: None,
module_registries,
module_registries_location,
- npm_api,
- npm_cache,
- npm_resolution,
- npm_resolver,
+ npm: LspNpmServices {
+ config_hash: LspNpmConfigHash(0), // this will be updated in initialize
+ api: npm_api,
+ cache: npm_cache,
+ resolution: npm_resolution,
+ resolver: npm_resolver,
+ },
performance,
ts_fixable_diagnostics: Default::default(),
ts_server,
@@ -622,7 +737,7 @@ impl Inner {
}?;
lsp_log!(" Resolved configuration file: \"{}\"", config_url);
- let config_file = ConfigFile::from_specifier(&config_url)?;
+ let config_file = ConfigFile::from_specifier(config_url)?;
return Ok(Some(config_file));
}
}
@@ -649,6 +764,10 @@ impl Inner {
&self,
maybe_config_file: Option<&ConfigFile>,
) -> Result<Option<PackageJson>, AnyError> {
+ if crate::args::has_flag_env_var("DENO_NO_PACKAGE_JSON") {
+ return Ok(None);
+ }
+
// It is possible that root_uri is not set, for example when having a single
// file open and not a workspace. In those situations we can't
// automatically discover the configuration
@@ -696,7 +815,7 @@ impl Inner {
&self,
tsconfig: &mut TsConfig,
) -> Result<(), AnyError> {
- if let Some(config_file) = self.maybe_config_file.as_ref() {
+ if let Some(config_file) = self.maybe_config_file() {
let (value, maybe_ignored_options) = config_file.to_compiler_options()?;
tsconfig.merge(&value);
if let Some(ignored_options) = maybe_ignored_options {
@@ -712,9 +831,9 @@ impl Inner {
pub fn snapshot(&self) -> Arc<StateSnapshot> {
// create a new snapshotted npm resolution and resolver
let npm_resolution = Arc::new(NpmResolution::new(
- self.npm_api.clone(),
- self.npm_resolution.snapshot(),
- None,
+ self.npm.api.clone(),
+ self.npm.resolution.snapshot(),
+ self.maybe_lockfile().cloned(),
));
let node_fs = Arc::new(deno_fs::RealFs);
let npm_resolver = Arc::new(CliNpmResolver::new(
@@ -722,14 +841,14 @@ impl Inner {
npm_resolution.clone(),
create_npm_fs_resolver(
node_fs.clone(),
- self.npm_cache.clone(),
+ self.npm.cache.clone(),
&ProgressBar::new(ProgressBarStyle::TextOnly),
- self.npm_api.base_url().clone(),
+ self.npm.api.base_url().clone(),
npm_resolution,
- None,
+ self.maybe_node_modules_dir_path().cloned(),
NpmSystemInfo::default(),
),
- None,
+ self.maybe_lockfile().cloned(),
));
let node_resolver =
Arc::new(NodeResolver::new(node_fs, npm_resolver.clone()));
@@ -743,7 +862,7 @@ impl Inner {
})
}
- pub fn update_cache(&mut self) -> Result<(), AnyError> {
+ pub async fn update_cache(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_cache", None::<()>);
self.performance.measure(mark);
let maybe_cache = &self.config.workspace_settings().cache;
@@ -773,20 +892,19 @@ impl Inner {
None
};
if self.maybe_cache_path != maybe_cache_path {
- self.recreate_http_client_and_dependents(maybe_cache_path)?;
+ self
+ .recreate_http_client_and_dependents(maybe_cache_path)
+ .await?;
}
Ok(())
}
/// Recreates the http client and all dependent structs.
- fn recreate_http_client_and_dependents(
+ async fn recreate_http_client_and_dependents(
&mut self,
new_cache_path: Option<PathBuf>,
) -> Result<(), AnyError> {
- let maybe_custom_root = new_cache_path
- .clone()
- .or_else(|| env::var("DENO_DIR").map(String::into).ok());
- let dir = DenoDir::new(maybe_custom_root)?;
+ let dir = self.deno_dir_from_maybe_cache_path(new_cache_path.clone())?;
let workspace_settings = self.config.workspace_settings();
let maybe_root_path = self
.config
@@ -812,12 +930,6 @@ impl Inner {
self.http_client.clone(),
);
self.module_registries_location = module_registries_location;
- (
- self.npm_api,
- self.npm_cache,
- self.npm_resolver,
- self.npm_resolution,
- ) = create_lsp_structs(&dir, self.http_client.clone());
// update the cache path
let location = dir.deps_folder_path();
self.documents.set_location(&location);
@@ -826,6 +938,64 @@ impl Inner {
Ok(())
}
+ fn deno_dir_from_maybe_cache_path(
+ &self,
+ cache_path: Option<PathBuf>,
+ ) -> std::io::Result<DenoDir> {
+ let maybe_custom_root =
+ cache_path.or_else(|| env::var("DENO_DIR").map(String::into).ok());
+ DenoDir::new(maybe_custom_root)
+ }
+
+ async fn recreate_npm_services_if_necessary(&mut self) {
+ let deno_dir = match self
+ .deno_dir_from_maybe_cache_path(self.maybe_cache_path.clone())
+ {
+ Ok(deno_dir) => deno_dir,
+ Err(err) => {
+ lsp_warn!("Error getting deno dir: {}", err);
+ return;
+ }
+ };
+ let config_hash = LspNpmConfigHash::from_inner(self);
+ if config_hash == self.npm.config_hash {
+ return; // no need to do anything
+ }
+
+ let registry_url = CliNpmRegistryApi::default_url();
+ let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
+ (self.npm.api, self.npm.cache) = create_npm_api_and_cache(
+ &deno_dir,
+ self.http_client.clone(),
+ registry_url,
+ &progress_bar,
+ );
+ let maybe_snapshot = match self.maybe_lockfile() {
+ Some(lockfile) => {
+ match snapshot_from_lockfile(lockfile.clone(), &self.npm.api).await {
+ Ok(snapshot) => Some(snapshot),
+ Err(err) => {
+ lsp_warn!("Failed getting npm snapshot from lockfile: {}", err);
+ None
+ }
+ }
+ }
+ None => None,
+ };
+ (self.npm.resolver, self.npm.resolution) =
+ create_npm_resolver_and_resolution(
+ registry_url,
+ progress_bar,
+ self.npm.api.clone(),
+ self.npm.cache.clone(),
+ self.maybe_node_modules_dir_path().cloned(),
+ maybe_snapshot,
+ );
+
+ // update the hash
+ self.npm.config_hash = config_hash;
+ }
+
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_import_map", None::<()>);
@@ -855,7 +1025,7 @@ impl Inner {
) -> Result<ImportMap, AnyError> {
resolve_import_map_from_specifier(
import_map_url,
- self.maybe_config_file.as_ref(),
+ self.maybe_config_file(),
&self.create_file_fetcher(cache_setting),
)
.await
@@ -896,7 +1066,7 @@ impl Inner {
"Setting import map from workspace settings: \"{}\"",
import_map_str
);
- if let Some(config_file) = &self.maybe_config_file {
+ if let Some(config_file) = self.maybe_config_file() {
if let Some(import_map_path) = config_file.to_import_map_path() {
lsp_log!("Warning: Import map \"{}\" configured in \"{}\" being ignored due to an import map being explicitly configured in workspace settings.", import_map_path, config_file.specifier);
}
@@ -922,7 +1092,7 @@ impl Inner {
import_map_str
));
}
- } else if let Some(config_file) = &self.maybe_config_file {
+ } else if let Some(config_file) = self.maybe_config_file() {
if config_file.is_an_import_map() {
lsp_log!(
"Setting import map defined in configuration file: \"{}\"",
@@ -968,7 +1138,9 @@ impl Inner {
async fn update_registries(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_registries", None::<()>);
- self.recreate_http_client_and_dependents(self.maybe_cache_path.clone())?;
+ self
+ .recreate_http_client_and_dependents(self.maybe_cache_path.clone())
+ .await?;
let workspace_settings = self.config.workspace_settings();
for (registry, enabled) in workspace_settings.suggest.imports.hosts.iter() {
if *enabled {
@@ -982,11 +1154,10 @@ impl Inner {
Ok(())
}
- fn update_config_file(&mut self) -> Result<(), AnyError> {
- self.maybe_config_file = None;
+ async fn update_config_file(&mut self) -> Result<(), AnyError> {
+ self.maybe_config_file_info = None;
self.fmt_options = Default::default();
self.lint_options = Default::default();
-
if let Some(config_file) = self.get_config_file()? {
let lint_options = config_file
.to_lint_config()
@@ -1005,7 +1176,11 @@ impl Inner {
anyhow!("Unable to update formatter configuration: {:?}", err)
})?;
- self.maybe_config_file = Some(config_file);
+ self.maybe_config_file_info = Some(LspConfigFileInfo {
+ maybe_lockfile: self.resolve_lockfile_from_config(&config_file),
+ maybe_node_modules_dir: resolve_node_modules_dir(&config_file),
+ config_file,
+ });
self.lint_options = lint_options;
self.fmt_options = fmt_options;
}
@@ -1013,12 +1188,45 @@ impl Inner {
Ok(())
}
+ fn resolve_lockfile_from_config(
+ &self,
+ config_file: &ConfigFile,
+ ) -> Option<Arc<Mutex<Lockfile>>> {
+ let lockfile_path = match config_file.resolve_lockfile_path() {
+ Ok(Some(value)) => value,
+ Ok(None) => return None,
+ Err(err) => {
+ lsp_warn!("Error resolving lockfile: {:#}", err);
+ return None;
+ }
+ };
+ resolve_lockfile_from_path(lockfile_path)
+ }
+
+ fn maybe_node_modules_dir_path(&self) -> Option<&PathBuf> {
+ self
+ .maybe_config_file_info
+ .as_ref()
+ .and_then(|p| p.maybe_node_modules_dir.as_ref())
+ }
+
+ fn maybe_config_file(&self) -> Option<&ConfigFile> {
+ self.maybe_config_file_info.as_ref().map(|c| &c.config_file)
+ }
+
+ fn maybe_lockfile(&self) -> Option<&Arc<Mutex<Lockfile>>> {
+ self
+ .maybe_config_file_info
+ .as_ref()
+ .and_then(|c| c.maybe_lockfile.as_ref())
+ }
+
/// Updates the package.json. Always ensure this is done after updating
/// the configuration file as the resolution of this depends on that.
fn update_package_json(&mut self) -> Result<(), AnyError> {
self.maybe_package_json = None;
self.maybe_package_json =
- self.get_package_json(self.maybe_config_file.as_ref())?;
+ self.get_package_json(self.maybe_config_file())?;
Ok(())
}
@@ -1126,10 +1334,10 @@ impl Inner {
self.update_debug_flag();
// Check to see if we need to change the cache path
- if let Err(err) = self.update_cache() {
+ if let Err(err) = self.update_cache().await {
self.client.show_message(MessageType::WARNING, err);
}
- if let Err(err) = self.update_config_file() {
+ if let Err(err) = self.update_config_file().await {
self.client.show_message(MessageType::WARNING, err);
}
if let Err(err) = self.update_package_json() {
@@ -1156,6 +1364,7 @@ impl Inner {
self.client.show_message(MessageType::WARNING, err);
}
+ self.recreate_npm_services_if_necessary().await;
self.assets.intitialize(self.snapshot()).await;
self.performance.measure(mark);
@@ -1174,10 +1383,13 @@ impl Inner {
.workspace_settings()
.document_preload_limit,
maybe_import_map: self.maybe_import_map.clone(),
- maybe_config_file: self.maybe_config_file.as_ref(),
+ maybe_config_file: self
+ .maybe_config_file_info
+ .as_ref()
+ .map(|c| &c.config_file),
maybe_package_json: self.maybe_package_json.as_ref(),
- npm_registry_api: self.npm_api.clone(),
- npm_resolution: self.npm_resolution.clone(),
+ npm_registry_api: self.npm.api.clone(),
+ npm_resolution: self.npm.resolution.clone(),
});
}
@@ -1245,7 +1457,7 @@ impl Inner {
async fn refresh_npm_specifiers(&mut self) {
let package_reqs = self.documents.npm_package_reqs();
- let npm_resolver = self.npm_resolver.clone();
+ let npm_resolver = self.npm.resolver.clone();
// spawn to avoid the LSP's Send requirements
let handle =
spawn(async move { npm_resolver.set_package_reqs(&package_reqs).await });
@@ -1303,13 +1515,13 @@ impl Inner {
}
self.update_debug_flag();
- if let Err(err) = self.update_cache() {
+ if let Err(err) = self.update_cache().await {
self.client.show_message(MessageType::WARNING, err);
}
if let Err(err) = self.update_registries().await {
self.client.show_message(MessageType::WARNING, err);
}
- if let Err(err) = self.update_config_file() {
+ if let Err(err) = self.update_config_file().await {
self.client.show_message(MessageType::WARNING, err);
}
if let Err(err) = self.update_package_json() {
@@ -1322,8 +1534,10 @@ impl Inner {
self.client.show_message(MessageType::WARNING, err);
}
+ self.recreate_npm_services_if_necessary().await;
self.refresh_documents_config();
+ self.diagnostics_server.invalidate_all();
self.send_diagnostics_update();
self.send_testing_update();
}
@@ -1343,9 +1557,9 @@ impl Inner {
.collect();
// if the current deno.json has changed, we need to reload it
- if let Some(config_file) = &self.maybe_config_file {
+ if let Some(config_file) = self.maybe_config_file() {
if changes.contains(&config_file.specifier) {
- if let Err(err) = self.update_config_file() {
+ if let Err(err) = self.update_config_file().await {
self.client.show_message(MessageType::WARNING, err);
}
if let Err(err) = self.update_tsconfig().await {
@@ -1354,6 +1568,24 @@ impl Inner {
touched = true;
}
}
+ if let Some(config_info) = self.maybe_config_file_info.as_mut() {
+ if let Some(lockfile) = config_info.maybe_lockfile.as_ref() {
+ let lockfile_path = lockfile.lock().filename.clone();
+ let maybe_specifier =
+ ModuleSpecifier::from_file_path(&lockfile_path).ok();
+ if let Some(specifier) = maybe_specifier {
+ if changes.contains(&specifier) {
+ let lockfile_hash = hash_lockfile(Some(lockfile));
+ let new_lockfile = resolve_lockfile_from_path(lockfile_path);
+ // only update if the lockfile has changed
+ if lockfile_hash != hash_lockfile(new_lockfile.as_ref()) {
+ config_info.maybe_lockfile = new_lockfile;
+ touched = true;
+ }
+ }
+ }
+ }
+ }
if let Some(package_json) = &self.maybe_package_json {
// always update the package json if the deno config changes
if touched || changes.contains(&package_json.specifier()) {
@@ -1374,6 +1606,7 @@ impl Inner {
}
}
if touched {
+ self.recreate_npm_services_if_necessary().await;
self.refresh_documents_config();
self.refresh_npm_specifiers().await;
self.diagnostics_server.invalidate_all();
@@ -2793,9 +3026,13 @@ impl tower_lsp::LanguageServer for LanguageServer {
}
}
- if !self.refresh_specifiers_from_client().await {
- // force update config
- self.0.write().await.refresh_documents_config();
+ self.refresh_specifiers_from_client().await;
+
+ {
+ let mut ls = self.0.write().await;
+ ls.refresh_documents_config();
+ ls.diagnostics_server.invalidate_all();
+ ls.send_diagnostics_update();
}
lsp_log!("Server ready.");
@@ -2949,7 +3186,12 @@ impl tower_lsp::LanguageServer for LanguageServer {
(ls.performance.clone(), mark)
};
- self.refresh_specifiers_from_client().await;
+ if self.refresh_specifiers_from_client().await {
+ let mut ls = self.0.write().await;
+ ls.refresh_documents_config();
+ ls.diagnostics_server.invalidate_all();
+ ls.send_diagnostics_update();
+ }
performance.measure(mark);
}
@@ -3147,23 +3389,24 @@ impl Inner {
vec![referrer]
};
+ let workspace_settings = self.config.workspace_settings();
let mut cli_options = CliOptions::new(
Flags {
cache_path: self.maybe_cache_path.clone(),
- ca_stores: None,
- ca_data: None,
- unsafely_ignore_certificate_errors: None,
- // this is to allow loading npm specifiers, so we can remove this
- // once stabilizing them
- unstable: true,
+ ca_stores: workspace_settings.certificate_stores.clone(),
+ ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
+ unsafely_ignore_certificate_errors: workspace_settings
+ .unsafely_ignore_certificate_errors
+ .clone(),
+ node_modules_dir: Some(self.maybe_node_modules_dir_path().is_some()),
+ // bit of a hack to force the lsp to cache the @types/node package
+ type_check_mode: crate::args::TypeCheckMode::Local,
..Default::default()
},
std::env::current_dir().with_context(|| "Failed getting cwd.")?,
- self.maybe_config_file.clone(),
- // TODO(#16510): add support for lockfile
- None,
- // TODO(bartlomieju): handle package.json dependencies here
- None,
+ self.maybe_config_file().cloned(),
+ self.maybe_lockfile().cloned(),
+ self.maybe_package_json.clone(),
)?;
cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone());
@@ -3195,12 +3438,7 @@ impl Inner {
}
fn get_tasks(&self) -> LspResult<Option<Value>> {
- Ok(
- self
- .maybe_config_file
- .as_ref()
- .and_then(|cf| cf.to_lsp_tasks()),
- )
+ Ok(self.maybe_config_file().and_then(|cf| cf.to_lsp_tasks()))
}
async fn inlay_hint(