summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2024-03-26 15:52:20 +0000
committerGitHub <noreply@github.com>2024-03-26 15:52:20 +0000
commit3b61104e2a8984255f74827b0f1c23476ccd8209 (patch)
tree43228946e39342e6c71f87ae720f93e7f8c63901 /cli/lsp/language_server.rs
parentd6452b39460ee46ade1ed9a5c95e469fd23a812c (diff)
refactor(lsp): unify config file data into ConfigTree (#23032)
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs763
1 files changed, 237 insertions, 526 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index de5f7e357..fafd9fe4c 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -18,11 +18,9 @@ use deno_lockfile::Lockfile;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver;
-use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_semver::jsr::JsrPackageReqReference;
-use import_map::ImportMap;
use indexmap::IndexSet;
use log::error;
use serde::Deserialize;
@@ -71,7 +69,6 @@ use super::documents::Document;
use super::documents::Documents;
use super::documents::DocumentsFilter;
use super::documents::LanguageId;
-use super::documents::UpdateDocumentConfigOptions;
use super::jsr::CliJsrSearchApi;
use super::logging::lsp_log;
use super::logging::lsp_warn;
@@ -92,16 +89,11 @@ use super::tsc::GetCompletionDetailsArgs;
use super::tsc::TsServer;
use super::urls;
use crate::args::get_root_cert_store;
-use crate::args::package_json;
-use crate::args::resolve_import_map;
use crate::args::CaData;
use crate::args::CacheSetting;
use crate::args::CliOptions;
use crate::args::ConfigFile;
use crate::args::Flags;
-use crate::args::FmtOptions;
-use crate::args::LintOptions;
-use crate::args::TsConfig;
use crate::cache::DenoDir;
use crate::cache::FastInsecureHasher;
use crate::cache::GlobalHttpCache;
@@ -111,6 +103,7 @@ use crate::factory::CliFactory;
use crate::file_fetcher::FileFetcher;
use crate::graph_util;
use crate::http_util::HttpClient;
+use crate::lsp::config::ConfigWatchedFileType;
use crate::lsp::logging::init_log_file;
use crate::lsp::tsc::file_text_changes_to_workspace_edit;
use crate::lsp::urls::LspUrlKind;
@@ -157,10 +150,15 @@ struct LspNpmConfigHash(u64);
impl LspNpmConfigHash {
pub fn from_inner(inner: &Inner) -> Self {
+ let config_data = inner.config.tree.root_data();
+ let node_modules_dir = config_data
+ .as_ref()
+ .and_then(|d| d.node_modules_dir.as_ref());
+ let lockfile = config_data.as_ref().and_then(|d| d.lockfile.as_ref());
let mut hasher = FastInsecureHasher::new();
- hasher.write_hashable(inner.config.maybe_node_modules_dir_path());
+ hasher.write_hashable(node_modules_dir);
hasher.write_hashable(&inner.maybe_global_cache_path);
- if let Some(lockfile) = inner.config.maybe_lockfile() {
+ if let Some(lockfile) = lockfile {
hasher.write_hashable(&*lockfile.lock());
}
Self(hasher.finish())
@@ -183,7 +181,6 @@ pub struct StateSnapshot {
pub cache_metadata: cache::CacheMetadata,
pub config: Arc<ConfigSnapshot>,
pub documents: Documents,
- pub maybe_import_map: Option<Arc<ImportMap>>,
pub npm: Option<StateNpmSnapshot>,
}
@@ -254,14 +251,6 @@ pub struct Inner {
/// An optional path to the DENO_DIR which has been specified in the client
/// options.
maybe_global_cache_path: Option<PathBuf>,
- /// An optional import map which is used to resolve modules.
- maybe_import_map: Option<Arc<ImportMap>>,
- /// An optional package.json configuration file.
- maybe_package_json: Option<PackageJson>,
- /// Configuration for formatter which has been taken from specified config file.
- fmt_options: FmtOptions,
- /// An optional configuration for linter which has been taken from specified config file.
- lint_options: LintOptions,
/// A lazily create "server" for handling test run requests.
maybe_testing_server: Option<testing::TestServer>,
/// Services used for dealing with npm related functionality.
@@ -344,7 +333,11 @@ impl LanguageServer {
// do as much as possible in a read, then do a write outside
let maybe_prepare_cache_result = {
let inner = self.0.read().await; // ensure dropped
- match inner.prepare_cache(specifiers, referrer, force_global_cache) {
+ match inner.prepare_cache(
+ specifiers,
+ referrer.clone(),
+ force_global_cache,
+ ) {
Ok(maybe_cache_result) => maybe_cache_result,
Err(err) => {
lsp_warn!("Error preparing caching: {:#}", err);
@@ -376,7 +369,7 @@ impl LanguageServer {
}
{
let mut inner = self.0.write().await;
- let lockfile = inner.config.maybe_lockfile().cloned();
+ let lockfile = inner.config.tree.lockfile_for_specifier(&referrer);
inner.documents.refresh_jsr_resolver(lockfile);
inner.refresh_npm_specifiers().await;
}
@@ -530,9 +523,12 @@ impl Inner {
let documents = Documents::new(deps_http_cache.clone());
let cache_metadata = cache::CacheMetadata::new(deps_http_cache.clone());
let performance = Arc::new(Performance::default());
- let ts_server =
- Arc::new(TsServer::new(performance.clone(), deps_http_cache.clone()));
- let config = Config::new();
+ let config = Config::default();
+ let ts_server = Arc::new(TsServer::new(
+ performance.clone(),
+ deps_http_cache.clone(),
+ config.tree.clone(),
+ ));
let diagnostics_state = Arc::new(DiagnosticsState::default());
let diagnostics_server = DiagnosticsServer::new(
client.clone(),
@@ -558,11 +554,7 @@ impl Inner {
initial_cwd: initial_cwd.clone(),
jsr_search_api,
maybe_global_cache_path: None,
- maybe_import_map: None,
- maybe_package_json: None,
- fmt_options: FmtOptions::new_with_base(initial_cwd.clone()),
task_queue: Default::default(),
- lint_options: LintOptions::new_with_base(initial_cwd),
maybe_testing_server: None,
module_registries,
module_registries_location,
@@ -645,76 +637,6 @@ impl Inner {
Ok(navigation_tree)
}
- fn get_config_file(&self) -> Result<Option<ConfigFile>, AnyError> {
- let workspace_settings = self.config.workspace_settings();
- let maybe_config = &workspace_settings.config;
- if let Some(config_str) = maybe_config {
- if !config_str.is_empty() {
- lsp_log!("Setting Deno configuration from: \"{}\"", config_str);
- let config_url = if let Ok(url) = Url::from_file_path(config_str) {
- Ok(url)
- } else if let Some(root_uri) = self.config.root_uri() {
- root_uri.join(config_str).map_err(|_| {
- anyhow!("Bad file path for configuration file: \"{}\"", config_str)
- })
- } else {
- Err(anyhow!(
- "The path to the configuration file (\"{}\") is not resolvable.",
- config_str
- ))
- }?;
- lsp_log!(" Resolved configuration file: \"{}\"", config_url);
-
- let config_file = ConfigFile::from_specifier(config_url)?;
- return Ok(Some(config_file));
- }
- }
-
- // Auto-discover config
-
- // 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
- if let Some(root_uri) = self.config.root_uri() {
- let root_path = specifier_to_file_path(root_uri)?;
- let mut checked = std::collections::HashSet::new();
- let maybe_config =
- ConfigFile::discover_from(&root_path, &mut checked, None)?;
- Ok(maybe_config.map(|c| {
- lsp_log!(" Auto-resolved configuration file: \"{}\"", c.specifier);
- c
- }))
- } else {
- Ok(None)
- }
- }
-
- fn get_package_json(
- &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
- if let Some(root_uri) = self.config.root_uri() {
- let root_path = specifier_to_file_path(root_uri)?;
- let maybe_package_json = package_json::discover_from(
- &root_path,
- maybe_config_file.and_then(|f| f.specifier.to_file_path().ok()),
- )?;
- Ok(maybe_package_json.map(|c| {
- lsp_log!(" Auto-resolved package.json: \"{}\"", c.specifier());
- c
- }))
- } else {
- Ok(None)
- }
- }
-
fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool {
if specifier.scheme() == "asset" {
matches!(
@@ -740,23 +662,6 @@ impl Inner {
}
}
- fn merge_user_tsconfig(
- &self,
- tsconfig: &mut TsConfig,
- ) -> Result<(), AnyError> {
- if let Some(config_file) = self.config.maybe_config_file() {
- let (value, maybe_ignored_options) = config_file.to_compiler_options()?;
- tsconfig.merge(&value);
- if let Some(ignored_options) = maybe_ignored_options {
- // TODO(@kitsonk) turn these into diagnostics that can be sent to the
- // client
- lsp_warn!("{}", ignored_options);
- }
- }
-
- Ok(())
- }
-
pub fn snapshot(&self) -> Arc<StateSnapshot> {
let maybe_state_npm_snapshot = self
.npm
@@ -785,7 +690,6 @@ impl Inner {
cache_metadata: self.cache_metadata.clone(),
config: self.config.snapshot(),
documents: self.documents.clone(),
- maybe_import_map: self.maybe_import_map.clone(),
npm: maybe_state_npm_snapshot,
})
}
@@ -876,7 +780,7 @@ impl Inner {
self.jsr_search_api = CliJsrSearchApi::new(deps_file_fetcher.clone());
self.npm.search_api = CliNpmSearchApi::new(deps_file_fetcher);
let maybe_local_cache =
- self.config.maybe_vendor_dir_path().map(|local_path| {
+ self.config.tree.root_vendor_dir().map(|local_path| {
Arc::new(LocalLspHttpCache::new(local_path, global_cache.clone()))
});
let cache: Arc<dyn HttpCache> = maybe_local_cache
@@ -903,14 +807,16 @@ impl Inner {
if config_hash == self.npm.config_hash {
return; // no need to do anything
}
-
+ let config_data = self.config.tree.root_data();
let npm_resolver = create_npm_resolver(
&deno_dir,
&self.initial_cwd,
&self.http_client,
- self.config.maybe_config_file(),
- self.config.maybe_lockfile(),
- self.config.maybe_node_modules_dir_path().cloned(),
+ config_data.as_ref().and_then(|d| d.config_file.as_deref()),
+ config_data.as_ref().and_then(|d| d.lockfile.as_ref()),
+ config_data
+ .as_ref()
+ .and_then(|d| d.node_modules_dir.clone()),
)
.await;
let node_resolver = Arc::new(NodeResolver::new(
@@ -929,41 +835,6 @@ impl Inner {
self.npm.config_hash = config_hash;
}
- pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
- let mark = self.performance.mark("lsp.update_import_map");
-
- let maybe_import_map_url = self.resolve_import_map_specifier()?;
- let maybe_import_map = self
- .fetch_import_map(
- maybe_import_map_url.as_ref(),
- CacheSetting::RespectHeaders,
- )
- .await?;
- if let Some(import_map) = maybe_import_map {
- if import_map.base_url().scheme() != "data" {
- lsp_log!(" Resolved import map: \"{}\"", import_map.base_url());
- }
- self.maybe_import_map = Some(Arc::new(import_map));
- } else {
- self.maybe_import_map = None;
- }
- self.performance.measure(mark);
- Ok(())
- }
-
- async fn fetch_import_map(
- &self,
- import_map_url: Option<&ModuleSpecifier>,
- cache_setting: CacheSetting,
- ) -> Result<Option<ImportMap>, AnyError> {
- resolve_import_map(
- import_map_url,
- self.config.maybe_config_file(),
- &self.create_file_fetcher(cache_setting),
- )
- .await
- }
-
fn create_file_fetcher(&self, cache_setting: CacheSetting) -> FileFetcher {
let mut file_fetcher = FileFetcher::new(
self.deps_http_cache.clone(),
@@ -979,10 +850,13 @@ impl Inner {
fn resolve_import_map_specifier(
&self,
+ referrer: &ModuleSpecifier,
) -> Result<Option<ModuleSpecifier>, AnyError> {
let Some(import_map_str) = self
.config
- .workspace_settings()
+ .settings
+ .get_for_specifier(referrer)
+ .0
.import_map
.clone()
.and_then(|s| if s.is_empty() { None } else { Some(s) })
@@ -993,7 +867,9 @@ impl Inner {
"Using import map from workspace settings: \"{}\"",
import_map_str
);
- if let Some(config_file) = self.config.maybe_config_file() {
+ if let Some(config_file) =
+ self.config.tree.config_file_for_specifier(referrer)
+ {
if let Some(import_map_path) = &config_file.json.import_map {
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);
}
@@ -1036,103 +912,6 @@ impl Inner {
self.performance.measure(mark);
Ok(())
}
-
- fn update_config_file(&mut self) -> Result<(), AnyError> {
- self.config.clear_config_file();
- self.fmt_options = FmtOptions::new_with_base(self.initial_cwd.clone());
- self.lint_options = LintOptions::new_with_base(self.initial_cwd.clone());
- if let Some(config_file) = self.get_config_file()? {
- let lint_options = config_file
- .to_lint_config()
- .and_then(|maybe_lint_config| {
- LintOptions::resolve(maybe_lint_config, None, &self.initial_cwd)
- })
- .map_err(|err| {
- anyhow!("Unable to update lint configuration: {:?}", err)
- })?;
- let fmt_options = config_file
- .to_fmt_config()
- .and_then(|maybe_fmt_config| {
- FmtOptions::resolve(maybe_fmt_config, None, &self.initial_cwd)
- })
- .map_err(|err| {
- anyhow!("Unable to update formatter configuration: {:?}", err)
- })?;
-
- self.config.set_config_file(config_file);
- self.lint_options = lint_options;
- self.fmt_options = fmt_options;
- self.recreate_http_client_and_dependents()?;
- if let Some(config_file) = self.config.maybe_config_file() {
- if let Ok((compiler_options, _)) = config_file.to_compiler_options() {
- if let Some(compiler_options_obj) = compiler_options.as_object() {
- if let Some(jsx_import_source) =
- compiler_options_obj.get("jsxImportSource")
- {
- if let Some(jsx_import_source) = jsx_import_source.as_str() {
- let specifiers = vec![Url::parse(&format!(
- "data:application/typescript;base64,{}",
- base64::engine::general_purpose::STANDARD.encode(format!(
- "import '{jsx_import_source}/jsx-runtime';"
- ))
- ))
- .unwrap()];
- let referrer = config_file.specifier.clone();
- self.task_queue.queue_task(Box::new(|ls: LanguageServer| {
- spawn(async move {
- if let Err(err) =
- ls.cache(specifiers, referrer, false).await
- {
- lsp_warn!("{:#}", err);
- }
- });
- }));
- }
- }
- }
- }
- }
- }
-
- Ok(())
- }
-
- /// 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.config.maybe_config_file())?;
- Ok(())
- }
-
- async fn update_tsconfig(&mut self) -> Result<(), AnyError> {
- let mark = self.performance.mark("lsp.update_tsconfig");
- let mut tsconfig = TsConfig::new(json!({
- "allowJs": true,
- "esModuleInterop": true,
- "experimentalDecorators": false,
- "isolatedModules": true,
- "jsx": "react",
- "lib": ["deno.ns", "deno.window", "deno.unstable"],
- "module": "esnext",
- "moduleDetection": "force",
- "noEmit": true,
- "resolveJsonModule": true,
- "strict": true,
- "target": "esnext",
- "useDefineForClassFields": true,
- // TODO(@kitsonk) remove for Deno 1.15
- "useUnknownInCatchVariables": false,
- }));
- if let Err(err) = self.merge_user_tsconfig(&mut tsconfig) {
- lsp_warn!("Error merging tsconfig: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- let _ok = self.ts_server.configure(self.snapshot(), tsconfig).await?;
- self.performance.measure(mark);
- Ok(())
- }
}
async fn create_npm_resolver(
@@ -1272,23 +1051,12 @@ impl Inner {
.start(self.config.internal_inspect().to_address());
self.update_debug_flag();
- // Check to see if we need to change the cache path
+ self.refresh_workspace_files();
+ self.refresh_config_tree().await;
if let Err(err) = self.update_cache() {
lsp_warn!("Error updating cache: {:#}", err);
self.client.show_message(MessageType::WARNING, err);
}
- if let Err(err) = self.update_config_file() {
- lsp_warn!("Error updating config file: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Err(err) = self.update_package_json() {
- lsp_warn!("Error updating package.json: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Err(err) = self.update_tsconfig().await {
- lsp_warn!("Error updating tsconfig: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
if capabilities.code_action_provider.is_some() {
let fixable_diagnostics = self
@@ -1298,11 +1066,6 @@ impl Inner {
self.ts_fixable_diagnostics = fixable_diagnostics;
}
- // Check to see if we need to setup the import map
- if let Err(err) = self.update_import_map().await {
- lsp_warn!("Error updating import map: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
// Check to see if we need to setup any module registries
if let Err(err) = self.update_registries().await {
lsp_warn!("Error updating registries: {:#}", err);
@@ -1450,15 +1213,58 @@ impl Inner {
self.workspace_files_hash = enable_settings_hash;
}
+ async fn refresh_config_tree(&mut self) {
+ let file_fetcher = self.create_file_fetcher(CacheSetting::RespectHeaders);
+ if let Some(root_uri) = self.config.root_uri() {
+ self
+ .config
+ .tree
+ .refresh(
+ &self.config.settings,
+ root_uri,
+ &self.workspace_files,
+ &file_fetcher,
+ )
+ .await;
+ for config_file in self.config.tree.config_files() {
+ if let Ok((compiler_options, _)) = config_file.to_compiler_options() {
+ if let Some(compiler_options_obj) = compiler_options.as_object() {
+ if let Some(jsx_import_source) =
+ compiler_options_obj.get("jsxImportSource")
+ {
+ if let Some(jsx_import_source) = jsx_import_source.as_str() {
+ let specifiers = vec![Url::parse(&format!(
+ "data:application/typescript;base64,{}",
+ base64::engine::general_purpose::STANDARD.encode(format!(
+ "import '{jsx_import_source}/jsx-runtime';"
+ ))
+ ))
+ .unwrap()];
+ let referrer = config_file.specifier.clone();
+ self.task_queue.queue_task(Box::new(|ls: LanguageServer| {
+ spawn(async move {
+ if let Err(err) =
+ ls.cache(specifiers, referrer, false).await
+ {
+ lsp_warn!("{:#}", err);
+ }
+ });
+ }));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
async fn refresh_documents_config(&mut self) {
- self.documents.update_config(UpdateDocumentConfigOptions {
- config: &self.config,
- maybe_import_map: self.maybe_import_map.clone(),
- maybe_package_json: self.maybe_package_json.as_ref(),
- node_resolver: self.npm.node_resolver.clone(),
- npm_resolver: self.npm.resolver.clone(),
- workspace_files: &self.workspace_files,
- });
+ self.documents.update_config(
+ &self.config,
+ self.npm.node_resolver.clone(),
+ self.npm.resolver.clone(),
+ &self.workspace_files,
+ );
// refresh the npm specifiers because it might have discovered
// a @types/node package and now's a good time to do that anyway
@@ -1591,6 +1397,8 @@ impl Inner {
};
self.update_debug_flag();
+ self.refresh_workspace_files();
+ self.refresh_config_tree().await;
if let Err(err) = self.update_cache() {
lsp_warn!("Error updating cache: {:#}", err);
self.client.show_message(MessageType::WARNING, err);
@@ -1599,27 +1407,8 @@ impl Inner {
lsp_warn!("Error updating registries: {:#}", err);
self.client.show_message(MessageType::WARNING, err);
}
- if let Err(err) = self.update_config_file() {
- lsp_warn!("Error updating config file: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Err(err) = self.update_package_json() {
- lsp_warn!("Error updating package.json: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Err(err) = self.update_import_map().await {
- lsp_warn!("Error updating import map: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Err(err) = self.update_tsconfig().await {
- lsp_warn!("Error updating tsconfig: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
-
self.recreate_npm_services_if_necessary().await;
- self.refresh_workspace_files();
self.refresh_documents_config().await;
-
self.diagnostics_server.invalidate_all();
self.send_diagnostics_update();
self.send_testing_update();
@@ -1629,194 +1418,73 @@ impl Inner {
&mut self,
params: DidChangeWatchedFilesParams,
) {
- fn has_lockfile_content_changed(lockfile: &Lockfile) -> bool {
- match Lockfile::new(lockfile.filename.clone(), false) {
- Ok(new_lockfile) => {
- // only update if the lockfile has changed
- FastInsecureHasher::hash(lockfile)
- != FastInsecureHasher::hash(new_lockfile)
- }
- Err(err) => {
- lsp_warn!("Error loading lockfile: {:#}", err);
- false
- }
- }
- }
-
- fn has_config_changed(config: &Config, changes: &IndexSet<Url>) -> bool {
- // Check the canonicalized specifier here because file watcher
- // changes will be for the canonicalized path in vscode, but also check the
- // non-canonicalized specifier in order to please the tests and handle
- // a client that might send that instead.
- if config
- .maybe_config_file_canonicalized_specifier()
- .map(|s| changes.contains(s))
- .unwrap_or(false)
- {
- return true;
- }
- match config.maybe_config_file() {
- Some(file) => {
- if changes.contains(&file.specifier) {
- return true;
- }
- }
- None => {
- // check for auto-discovery
- if changes.iter().any(|url| {
- url.path().ends_with("/deno.json")
- || url.path().ends_with("/deno.jsonc")
- }) {
- return true;
- }
- }
- }
-
- // if the lockfile has changed, reload the config as well
- if let Some(lockfile) = config.maybe_lockfile() {
- let lockfile_matches = config
- .maybe_lockfile_canonicalized_specifier()
- .map(|s| changes.contains(s))
- .or_else(|| {
- ModuleSpecifier::from_file_path(&lockfile.lock().filename)
- .ok()
- .map(|s| changes.contains(&s))
- })
- .unwrap_or(false);
- lockfile_matches && has_lockfile_content_changed(&lockfile.lock())
- } else {
- // check for auto-discovery
- changes.iter().any(|url| url.path().ends_with("/deno.lock"))
- }
- }
-
let mark = self
.performance
.mark_with_args("lsp.did_change_watched_files", &params);
- let mut touched = false;
- let changes: IndexSet<Url> = params
- .changes
- .iter()
- .map(|f| self.url_map.normalize_url(&f.uri, LspUrlKind::File))
- .collect();
- let mut config_changes = IndexSet::with_capacity(changes.len());
-
- // if the current deno.json has changed, we need to reload it
- if has_config_changed(&self.config, &changes) {
- // Check the 'current' config specifier from both before and after it's
- // updated. Check canonicalized and uncanonicalized variants for each.
- // If any are included in `changes`, send our custom notification for
- // `deno.json` changes: `deno/didChangeDenoConfigurationNotification`.
- let mut files_to_check = IndexSet::with_capacity(4);
- // Collect previous config specifiers.
- if let Some(url) = self.config.maybe_config_file().map(|c| &c.specifier) {
- files_to_check.insert(url.clone());
- }
- if let Some(url) = self.config.maybe_config_file_canonicalized_specifier()
- {
- files_to_check.insert(url.clone());
- }
- // Update config.
- if let Err(err) = self.update_config_file() {
- lsp_warn!("Error updating config file: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- // Collect new config specifiers.
- if let Some(url) = self.config.maybe_config_file().map(|c| &c.specifier) {
- files_to_check.insert(url.clone());
- }
- if let Some(url) = self.config.maybe_config_file_canonicalized_specifier()
- {
- files_to_check.insert(url.clone());
- }
- if let Some(root_uri) = self.config.root_uri() {
- config_changes.extend(
- params
- .changes
- .iter()
- .filter(|e| files_to_check.contains(&e.uri))
- .map(|e| lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: root_uri.clone(),
- file_uri: e.uri.clone(),
- typ:
- lsp_custom::DenoConfigurationChangeType::from_file_change_type(
- e.typ,
- ),
- configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
- }),
- );
- }
- if let Err(err) = self.update_tsconfig().await {
- lsp_warn!("Error updating tsconfig: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- touched = true;
- }
-
- let has_package_json_changed = changes
+ let changes = params
+ .changes
+ .into_iter()
+ .map(|e| (self.url_map.normalize_url(&e.uri, LspUrlKind::File), e))
+ .collect::<Vec<_>>();
+ if changes
.iter()
- .any(|e| e.as_str().ends_with("/package.json"));
-
- if has_package_json_changed {
- let mut files_to_check = IndexSet::with_capacity(2);
- if let Some(package_json) = &self.maybe_package_json {
- files_to_check.insert(package_json.specifier());
- }
- if let Err(err) = self.update_package_json() {
- lsp_warn!("Error updating package.json: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- if let Some(package_json) = &self.maybe_package_json {
- files_to_check.insert(package_json.specifier());
- }
- if let Some(root_uri) = self.config.root_uri() {
- config_changes.extend(
- params
- .changes
- .iter()
- .filter(|e| files_to_check.contains(&e.uri))
- .map(|e| lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: root_uri.clone(),
- file_uri: e.uri.clone(),
- typ:
- lsp_custom::DenoConfigurationChangeType::from_file_change_type(
- e.typ,
- ),
- configuration_type:
- lsp_custom::DenoConfigurationType::PackageJson,
- }),
+ .any(|(s, _)| self.config.tree.is_watched_file(s))
+ {
+ let mut deno_config_changes = IndexSet::with_capacity(changes.len());
+ deno_config_changes.extend(changes.iter().filter_map(|(s, e)| {
+ self.config.tree.watched_file_type(s).and_then(|t| {
+ let configuration_type = match t.1 {
+ ConfigWatchedFileType::DenoJson => {
+ lsp_custom::DenoConfigurationType::DenoJson
+ }
+ ConfigWatchedFileType::PackageJson => {
+ lsp_custom::DenoConfigurationType::PackageJson
+ }
+ _ => return None,
+ };
+ Some(lsp_custom::DenoConfigurationChangeEvent {
+ scope_uri: t.0,
+ file_uri: e.uri.clone(),
+ typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
+ e.typ,
+ ),
+ configuration_type,
+ })
+ })
+ }));
+ self.workspace_files_hash = 0;
+ self.refresh_workspace_files();
+ self.refresh_config_tree().await;
+ deno_config_changes.extend(changes.iter().filter_map(|(s, e)| {
+ self.config.tree.watched_file_type(s).and_then(|t| {
+ let configuration_type = match t.1 {
+ ConfigWatchedFileType::DenoJson => {
+ lsp_custom::DenoConfigurationType::DenoJson
+ }
+ ConfigWatchedFileType::PackageJson => {
+ lsp_custom::DenoConfigurationType::PackageJson
+ }
+ _ => return None,
+ };
+ Some(lsp_custom::DenoConfigurationChangeEvent {
+ scope_uri: t.0,
+ file_uri: e.uri.clone(),
+ typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
+ e.typ,
+ ),
+ configuration_type,
+ })
+ })
+ }));
+ if !deno_config_changes.is_empty() {
+ self.client.send_did_change_deno_configuration_notification(
+ lsp_custom::DidChangeDenoConfigurationNotificationParams {
+ changes: deno_config_changes.into_iter().collect(),
+ },
);
}
- touched = true;
- }
-
- if !config_changes.is_empty() {
- self.client.send_did_change_deno_configuration_notification(
- lsp_custom::DidChangeDenoConfigurationNotificationParams {
- changes: config_changes.into_iter().collect(),
- },
- );
- }
-
- // if the current import map, or config file has changed, we need to
- // reload the import map
- let import_map_changed = self
- .maybe_import_map
- .as_ref()
- .map(|import_map| changes.contains(import_map.base_url()))
- .unwrap_or(false);
- if touched || import_map_changed {
- if let Err(err) = self.update_import_map().await {
- lsp_warn!("Error updating import map: {:#}", err);
- self.client.show_message(MessageType::WARNING, err);
- }
- touched = true;
- }
-
- if touched {
self.recreate_npm_services_if_necessary().await;
- self.refresh_workspace_files();
self.refresh_documents_config().await;
self.diagnostics_server.invalidate_all();
self.ts_server.restart(self.snapshot()).await;
@@ -1902,7 +1570,13 @@ impl Inner {
.url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File);
// skip formatting any files ignored by the config file
- if !self.fmt_options.files.matches_specifier(&specifier) {
+ if !self
+ .config
+ .tree
+ .fmt_options_for_specifier(&specifier)
+ .files
+ .matches_specifier(&specifier)
+ {
return Ok(None);
}
let document = match self.documents.get(&specifier) {
@@ -1925,7 +1599,12 @@ impl Inner {
// spawn a blocking task to allow doing other work while this is occurring
let text_edits = deno_core::unsync::spawn_blocking({
- let fmt_options = self.fmt_options.options.clone();
+ let fmt_options = self
+ .config
+ .tree
+ .fmt_options_for_specifier(&specifier)
+ .options
+ .clone();
let document = document.clone();
move || {
let format_result = match document.maybe_parsed_source() {
@@ -2152,10 +1831,14 @@ impl Inner {
line_index.offset_tsc(diagnostic.range.start)?
..line_index.offset_tsc(diagnostic.range.end)?,
codes,
- (&self.fmt_options.options).into(),
+ (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(&specifier)
+ .options)
+ .into(),
tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&specifier,
),
)
@@ -2246,7 +1929,6 @@ impl Inner {
..line_index.offset_tsc(params.range.end)?,
Some(tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&specifier,
)),
only,
@@ -2305,10 +1987,14 @@ impl Inner {
.get_combined_code_fix(
self.snapshot(),
&code_action_data,
- (&self.fmt_options.options).into(),
+ (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(&code_action_data.specifier)
+ .options)
+ .into(),
tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&code_action_data.specifier,
),
)
@@ -2322,7 +2008,7 @@ impl Inner {
fix_ts_import_changes(
&code_action_data.specifier,
&combined_code_actions.changes,
- &self.get_ts_response_import_mapper(),
+ &self.get_ts_response_import_mapper(&code_action_data.specifier),
)
.map_err(|err| {
error!("Unable to remap changes: {:#}", err);
@@ -2351,14 +2037,18 @@ impl Inner {
.get_edits_for_refactor(
self.snapshot(),
action_data.specifier.clone(),
- (&self.fmt_options.options).into(),
+ (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(&action_data.specifier)
+ .options)
+ .into(),
line_index.offset_tsc(action_data.range.start)?
..line_index.offset_tsc(action_data.range.end)?,
action_data.refactor_name,
action_data.action_name,
Some(tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&action_data.specifier,
)),
)
@@ -2374,10 +2064,13 @@ impl Inner {
Ok(result)
}
- pub fn get_ts_response_import_mapper(&self) -> TsResponseImportMapper {
+ pub fn get_ts_response_import_mapper(
+ &self,
+ referrer: &ModuleSpecifier,
+ ) -> TsResponseImportMapper {
TsResponseImportMapper::new(
&self.documents,
- self.maybe_import_map.as_deref(),
+ self.config.tree.import_map_for_specifier(referrer),
self.npm.node_resolver.as_deref(),
self.npm.resolver.as_deref(),
)
@@ -2690,7 +2383,7 @@ impl Inner {
&self.jsr_search_api,
&self.npm.search_api,
&self.documents,
- self.maybe_import_map.clone(),
+ self.config.tree.import_map_for_specifier(&specifier),
)
.await;
}
@@ -2716,13 +2409,17 @@ impl Inner {
tsc::GetCompletionsAtPositionOptions {
user_preferences: tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&specifier,
),
trigger_character,
trigger_kind,
},
- (&self.fmt_options.options).into(),
+ (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(&specifier)
+ .options)
+ .into(),
)
.await;
@@ -2769,11 +2466,17 @@ impl Inner {
.get_completion_details(
self.snapshot(),
GetCompletionDetailsArgs {
- format_code_settings: Some((&self.fmt_options.options).into()),
+ format_code_settings: Some(
+ (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(specifier)
+ .options)
+ .into(),
+ ),
preferences: Some(
tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
specifier,
),
),
@@ -3289,6 +2992,12 @@ impl Inner {
if options.enabled == UpdateImportsOnFileMoveEnabled::Never {
continue;
}
+ let format_code_settings = (&self
+ .config
+ .tree
+ .fmt_options_for_specifier(&old_specifier)
+ .options)
+ .into();
changes.extend(
self
.ts_server
@@ -3299,7 +3008,7 @@ impl Inner {
&resolve_url(&rename.new_uri).unwrap(),
LspUrlKind::File,
),
- (&self.fmt_options.options).into(),
+ format_code_settings,
tsc::UserPreferences {
allow_text_changes_in_new_files: Some(true),
..Default::default()
@@ -3350,7 +3059,6 @@ impl Inner {
let snapshot = DiagnosticServerUpdateMessage {
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) {
@@ -3459,31 +3167,31 @@ impl tower_lsp::LanguageServer for LanguageServer {
ls.maybe_testing_server = Some(test_server);
}
- if let Some(root_uri) = ls.config.root_uri() {
- let mut config_events = vec![];
- if let Some(config_file) = ls.config.maybe_config_file() {
+ let mut config_events = vec![];
+ for (scope_uri, config_data) in ls.config.tree.data_by_scope() {
+ if let Some(config_file) = &config_data.config_file {
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: root_uri.clone(),
+ scope_uri: scope_uri.clone(),
file_uri: config_file.specifier.clone(),
typ: lsp_custom::DenoConfigurationChangeType::Added,
configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
});
}
- if let Some(package_json) = &ls.maybe_package_json {
+ if let Some(package_json) = &config_data.package_json {
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: root_uri.clone(),
+ scope_uri: scope_uri.clone(),
file_uri: package_json.specifier(),
typ: lsp_custom::DenoConfigurationChangeType::Added,
configuration_type: lsp_custom::DenoConfigurationType::PackageJson,
});
}
- if !config_events.is_empty() {
- ls.client.send_did_change_deno_configuration_notification(
- lsp_custom::DidChangeDenoConfigurationNotificationParams {
- changes: config_events,
- },
- );
- }
+ }
+ if !config_events.is_empty() {
+ ls.client.send_did_change_deno_configuration_notification(
+ lsp_custom::DidChangeDenoConfigurationNotificationParams {
+ changes: config_events,
+ },
+ );
}
(ls.client.clone(), ls.http_client.clone())
@@ -3504,11 +3212,6 @@ impl tower_lsp::LanguageServer for LanguageServer {
{
let mut ls = self.0.write().await;
init_log_file(ls.config.log_file());
- if let Err(err) = ls.update_tsconfig().await {
- lsp_warn!("Error updating tsconfig: {:#}", err);
- ls.client.show_message(MessageType::WARNING, err);
- }
- ls.refresh_workspace_files();
ls.refresh_documents_config().await;
ls.diagnostics_server.invalidate_all();
ls.send_diagnostics_update();
@@ -3643,6 +3346,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
{
let mut ls = self.0.write().await;
ls.refresh_workspace_files();
+ ls.refresh_config_tree().await;
ls.refresh_documents_config().await;
ls.diagnostics_server.invalidate_all();
ls.send_diagnostics_update();
@@ -3845,10 +3549,11 @@ impl Inner {
let mark = self
.performance
.mark_with_args("lsp.cache", (&specifiers, &referrer));
+ let config_data = self.config.tree.data_for_specifier(&referrer);
let roots = if !specifiers.is_empty() {
specifiers
} else {
- vec![referrer]
+ vec![referrer.clone()]
};
let workspace_settings = self.config.workspace_settings();
let mut cli_options = CliOptions::new(
@@ -3860,21 +3565,28 @@ impl Inner {
.unsafely_ignore_certificate_errors
.clone(),
node_modules_dir: Some(
- self.config.maybe_node_modules_dir_path().is_some(),
+ config_data
+ .as_ref()
+ .and_then(|d| d.node_modules_dir.as_ref())
+ .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()
},
self.initial_cwd.clone(),
- self.config.maybe_config_file().cloned(),
- self.config.maybe_lockfile().cloned(),
- self.maybe_package_json.clone(),
+ config_data
+ .as_ref()
+ .and_then(|d| d.config_file.as_deref().cloned()),
+ config_data.as_ref().and_then(|d| d.lockfile.clone()),
+ config_data
+ .as_ref()
+ .and_then(|d| d.package_json.as_deref().cloned()),
force_global_cache,
)?;
// don't use the specifier in self.maybe_import_map because it's not
// necessarily an import map specifier (could be a deno.json)
- if let Some(import_map) = self.resolve_import_map_specifier()? {
+ if let Some(import_map) = self.resolve_import_map_specifier(&referrer)? {
cli_options.set_import_map_specifier(Some(import_map));
}
@@ -3907,7 +3619,7 @@ impl Inner {
fn task_definitions(&self) -> LspResult<Vec<TaskDefinition>> {
let mut result = vec![];
- if let Some(config_file) = self.config.maybe_config_file() {
+ for config_file in self.config.tree.config_files() {
if let Some(tasks) = json!(&config_file.json.tasks).as_object() {
for (name, value) in tasks {
let Some(command) = value.as_str() else {
@@ -3921,7 +3633,7 @@ impl Inner {
}
};
}
- if let Some(package_json) = &self.maybe_package_json {
+ for package_json in self.config.tree.package_jsons() {
if let Some(scripts) = &package_json.scripts {
for (name, command) in scripts {
result.push(TaskDefinition {
@@ -3968,7 +3680,6 @@ impl Inner {
text_span,
tsc::UserPreferences::from_config_for_specifier(
&self.config,
- &self.fmt_options.options,
&specifier,
),
)