summaryrefslogtreecommitdiff
path: root/cli/lsp/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/config.rs')
-rw-r--r--cli/lsp/config.rs388
1 files changed, 149 insertions, 239 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index a3d8d2ad6..a87c49108 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -2,12 +2,11 @@
use super::logging::lsp_log;
use crate::args::ConfigFile;
+use crate::cache::FastInsecureHasher;
use crate::lsp::logging::lsp_warn;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::path::specifier_to_file_path;
use deno_ast::MediaType;
-use deno_config::glob::PathOrPattern;
-use deno_config::glob::PathOrPatternSet;
use deno_config::FmtOptionsConfig;
use deno_core::parking_lot::Mutex;
use deno_core::serde::de::DeserializeOwned;
@@ -733,12 +732,17 @@ impl ConfigSnapshot {
/// Determine if the provided specifier is enabled or not.
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
- specifier_enabled(
- specifier,
- self.config_file.as_ref(),
- &self.settings,
- &self.workspace_folders,
- )
+ if let Some(cf) = &self.config_file {
+ if let Ok(files) = cf.to_files_config() {
+ if !files.matches_specifier(specifier) {
+ return false;
+ }
+ }
+ }
+ self
+ .settings
+ .specifier_enabled(specifier)
+ .unwrap_or_else(|| self.config_file.is_some())
}
pub fn specifier_enabled_for_test(
@@ -759,10 +763,55 @@ impl ConfigSnapshot {
#[derive(Debug, Default, Clone)]
pub struct Settings {
pub unscoped: WorkspaceSettings,
- pub by_workspace_folder: Option<BTreeMap<ModuleSpecifier, WorkspaceSettings>>,
+ pub by_workspace_folder: BTreeMap<ModuleSpecifier, Option<WorkspaceSettings>>,
}
impl Settings {
+ pub fn first_root_uri(&self) -> Option<&ModuleSpecifier> {
+ self.by_workspace_folder.first_key_value().map(|e| e.0)
+ }
+
+ /// Returns `None` if the value should be deferred to the presence of a
+ /// `deno.json` file.
+ pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> Option<bool> {
+ let Ok(path) = specifier_to_file_path(specifier) else {
+ // Non-file URLs are not disabled by these settings.
+ return Some(true);
+ };
+ let (settings, mut folder_uri) = self.get_for_specifier(specifier);
+ folder_uri = folder_uri.or_else(|| self.first_root_uri());
+ let mut disable_paths = vec![];
+ let mut enable_paths = None;
+ if let Some(folder_uri) = folder_uri {
+ if let Ok(folder_path) = specifier_to_file_path(folder_uri) {
+ disable_paths = settings
+ .disable_paths
+ .iter()
+ .map(|p| folder_path.join(p))
+ .collect::<Vec<_>>();
+ enable_paths = settings.enable_paths.as_ref().map(|enable_paths| {
+ enable_paths
+ .iter()
+ .map(|p| folder_path.join(p))
+ .collect::<Vec<_>>()
+ });
+ }
+ }
+
+ if disable_paths.iter().any(|p| path.starts_with(p)) {
+ Some(false)
+ } else if let Some(enable_paths) = &enable_paths {
+ for enable_path in enable_paths {
+ if path.starts_with(enable_path) {
+ return Some(true);
+ }
+ }
+ Some(false)
+ } else {
+ settings.enable
+ }
+ }
+
pub fn get_unscoped(&self) -> &WorkspaceSettings {
&self.unscoped
}
@@ -774,8 +823,14 @@ impl Settings {
let Ok(path) = specifier_to_file_path(specifier) else {
return (&self.unscoped, None);
};
- if let Some(by_workspace_folder) = &self.by_workspace_folder {
- for (folder_uri, settings) in by_workspace_folder.iter().rev() {
+ let mut is_first_folder = true;
+ for (folder_uri, settings) in self.by_workspace_folder.iter().rev() {
+ let mut settings = settings.as_ref();
+ if is_first_folder {
+ settings = settings.or(Some(&self.unscoped));
+ }
+ is_first_folder = false;
+ if let Some(settings) = settings {
let Ok(folder_path) = specifier_to_file_path(folder_uri) else {
continue;
};
@@ -786,6 +841,24 @@ impl Settings {
}
(&self.unscoped, None)
}
+
+ pub fn enable_settings_hash(&self) -> u64 {
+ let mut hasher = FastInsecureHasher::default();
+ let unscoped = self.get_unscoped();
+ hasher.write_hashable(unscoped.enable);
+ hasher.write_hashable(&unscoped.enable_paths);
+ hasher.write_hashable(&unscoped.disable_paths);
+ hasher.write_hashable(unscoped.document_preload_limit);
+ for (folder_uri, settings) in &self.by_workspace_folder {
+ hasher.write_hashable(folder_uri);
+ hasher.write_hashable(
+ settings
+ .as_ref()
+ .map(|s| (&s.enable, &s.enable_paths, &s.disable_paths)),
+ );
+ }
+ hasher.finish()
+ }
}
#[derive(Debug)]
@@ -808,7 +881,7 @@ struct LspConfigFileInfo {
#[derive(Debug)]
pub struct Config {
pub client_capabilities: ClientCapabilities,
- settings: Settings,
+ pub settings: Settings,
pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>,
/// An optional configuration file which has been specified in the client
/// options along with some data that is computed after the config file is set.
@@ -827,29 +900,46 @@ impl Config {
}
#[cfg(test)]
- pub fn new_with_root(root_uri: Url) -> Self {
+ pub fn new_with_roots(root_uris: impl IntoIterator<Item = Url>) -> Self {
let mut config = Self::new();
- let name = root_uri.path_segments().and_then(|s| s.last());
- let name = name.unwrap_or_default().to_string();
- config.workspace_folders = vec![(
- root_uri.clone(),
- lsp::WorkspaceFolder {
- uri: root_uri,
- name,
- },
- )];
+ let mut folders = vec![];
+ for root_uri in root_uris {
+ let name = root_uri.path_segments().and_then(|s| s.last());
+ let name = name.unwrap_or_default().to_string();
+ folders.push((
+ root_uri.clone(),
+ lsp::WorkspaceFolder {
+ uri: root_uri,
+ name,
+ },
+ ));
+ }
+ config.set_workspace_folders(folders);
config
}
+ pub fn set_workspace_folders(
+ &mut self,
+ folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>,
+ ) {
+ self.settings.by_workspace_folder =
+ folders.iter().map(|(s, _)| (s.clone(), None)).collect();
+ self.workspace_folders = folders;
+ }
+
pub fn set_workspace_settings(
&mut self,
unscoped: WorkspaceSettings,
- by_workspace_folder: Option<BTreeMap<ModuleSpecifier, WorkspaceSettings>>,
+ folder_settings: Vec<(ModuleSpecifier, WorkspaceSettings)>,
) {
- self.settings = Settings {
- unscoped,
- by_workspace_folder,
- };
+ self.settings.unscoped = unscoped;
+ for (folder_uri, settings) in folder_settings.into_iter() {
+ if let Some(settings_) =
+ self.settings.by_workspace_folder.get_mut(&folder_uri)
+ {
+ *settings_ = Some(settings);
+ }
+ }
}
pub fn workspace_settings(&self) -> &WorkspaceSettings {
@@ -1006,12 +1096,18 @@ impl Config {
}
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
- specifier_enabled(
- specifier,
- self.maybe_config_file(),
- &self.settings,
- &self.workspace_folders,
- )
+ let config_file = self.maybe_config_file();
+ if let Some(cf) = config_file {
+ if let Ok(files) = cf.to_files_config() {
+ if !files.matches_specifier(specifier) {
+ return false;
+ }
+ }
+ }
+ self
+ .settings
+ .specifier_enabled(specifier)
+ .unwrap_or_else(|| config_file.is_some())
}
pub fn specifier_enabled_for_test(
@@ -1025,71 +1121,7 @@ impl Config {
}
}
}
- if !self.specifier_enabled(specifier) {
- return false;
- }
- true
- }
-
- pub fn get_enabled_paths(&self) -> PathOrPatternSet {
- let mut paths = vec![];
- for (workspace_uri, _) in &self.workspace_folders {
- let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
- lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
- continue;
- };
- let settings = self.workspace_settings_for_specifier(workspace_uri);
- if let Some(enable_paths) = &settings.enable_paths {
- for path in enable_paths {
- match PathOrPattern::from_relative(&workspace_path, path) {
- Ok(path_or_pattern) => paths.push(path_or_pattern),
- Err(err) => {
- lsp_log!("Invalid enable path '{}': {:#}", path, err);
- }
- }
- }
- } else {
- paths.push(PathOrPattern::Path(workspace_path));
- }
- }
- paths.sort();
- paths.dedup();
- PathOrPatternSet::new(paths)
- }
-
- pub fn get_disabled_paths(&self) -> PathOrPatternSet {
- let mut path_or_patterns = vec![];
- if let Some(cf) = self.maybe_config_file() {
- if let Ok(files) = cf.to_files_config() {
- for path in files.exclude.into_path_or_patterns() {
- path_or_patterns.push(path);
- }
- }
- }
- for (workspace_uri, _) in &self.workspace_folders {
- let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
- lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
- continue;
- };
- let settings = self.workspace_settings_for_specifier(workspace_uri);
- let is_enabled = settings
- .enable_paths
- .as_ref()
- .map(|p| !p.is_empty())
- .unwrap_or_else(|| {
- settings.enable.unwrap_or_else(|| self.has_config_file())
- });
- if is_enabled {
- for path in &settings.disable_paths {
- path_or_patterns.push(PathOrPattern::Path(workspace_path.join(path)));
- }
- } else {
- path_or_patterns.push(PathOrPattern::Path(workspace_path));
- }
- }
- path_or_patterns.sort();
- path_or_patterns.dedup();
- PathOrPatternSet::new(path_or_patterns)
+ self.specifier_enabled(specifier)
}
pub fn log_file(&self) -> bool {
@@ -1154,57 +1186,6 @@ impl Config {
}
}
-fn specifier_enabled(
- specifier: &Url,
- config_file: Option<&ConfigFile>,
- settings: &Settings,
- workspace_folders: &[(Url, lsp::WorkspaceFolder)],
-) -> bool {
- if let Some(cf) = config_file {
- if let Ok(files) = cf.to_files_config() {
- if !files.matches_specifier(specifier) {
- return false;
- }
- }
- }
- let Ok(path) = specifier_to_file_path(specifier) else {
- // Non-file URLs are not disabled by these settings.
- return true;
- };
- let (settings, mut folder_uri) = settings.get_for_specifier(specifier);
- folder_uri = folder_uri.or_else(|| workspace_folders.first().map(|f| &f.0));
- let mut disable_paths = vec![];
- let mut enable_paths = None;
- if let Some(folder_uri) = folder_uri {
- if let Ok(folder_path) = specifier_to_file_path(folder_uri) {
- disable_paths = settings
- .disable_paths
- .iter()
- .map(|p| folder_path.join(p))
- .collect::<Vec<_>>();
- enable_paths = settings.enable_paths.as_ref().map(|enable_paths| {
- enable_paths
- .iter()
- .map(|p| folder_path.join(p))
- .collect::<Vec<_>>()
- });
- }
- }
- if let Some(enable_paths) = &enable_paths {
- for enable_path in enable_paths {
- if path.starts_with(enable_path)
- && !disable_paths.iter().any(|p| path.starts_with(p))
- {
- return true;
- }
- }
- false
- } else {
- settings.enable.unwrap_or_else(|| config_file.is_some())
- && !disable_paths.iter().any(|p| path.starts_with(p))
- }
-}
-
fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> {
let lockfile_path = match config_file.resolve_lockfile_path() {
Ok(Some(value)) => value,
@@ -1265,7 +1246,7 @@ mod tests {
#[test]
fn test_config_specifier_enabled() {
let root_uri = resolve_url("file:///").unwrap();
- let mut config = Config::new_with_root(root_uri);
+ let mut config = Config::new_with_roots(vec![root_uri]);
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
config.set_workspace_settings(
@@ -1273,7 +1254,7 @@ mod tests {
"enable": true
}))
.unwrap(),
- None,
+ vec![],
);
assert!(config.specifier_enabled(&specifier));
}
@@ -1281,7 +1262,7 @@ mod tests {
#[test]
fn test_config_snapshot_specifier_enabled() {
let root_uri = resolve_url("file:///").unwrap();
- let mut config = Config::new_with_root(root_uri);
+ let mut config = Config::new_with_roots(vec![root_uri]);
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
config.set_workspace_settings(
@@ -1289,7 +1270,7 @@ mod tests {
"enable": true
}))
.unwrap(),
- None,
+ vec![],
);
let config_snapshot = config.snapshot();
assert!(config_snapshot.specifier_enabled(&specifier));
@@ -1298,14 +1279,14 @@ mod tests {
#[test]
fn test_config_specifier_enabled_path() {
let root_uri = resolve_url("file:///project/").unwrap();
- let mut config = Config::new_with_root(root_uri);
+ let mut config = Config::new_with_roots(vec![root_uri]);
let specifier_a = resolve_url("file:///project/worker/a.ts").unwrap();
let specifier_b = resolve_url("file:///project/other/b.ts").unwrap();
assert!(!config.specifier_enabled(&specifier_a));
assert!(!config.specifier_enabled(&specifier_b));
let workspace_settings =
serde_json::from_str(r#"{ "enablePaths": ["worker"] }"#).unwrap();
- config.set_workspace_settings(workspace_settings, None);
+ config.set_workspace_settings(workspace_settings, vec![]);
assert!(config.specifier_enabled(&specifier_a));
assert!(!config.specifier_enabled(&specifier_b));
let config_snapshot = config.snapshot();
@@ -1316,7 +1297,7 @@ mod tests {
#[test]
fn test_config_specifier_disabled_path() {
let root_uri = resolve_url("file:///root/").unwrap();
- let mut config = Config::new_with_root(root_uri.clone());
+ let mut config = Config::new_with_roots(vec![root_uri.clone()]);
config.settings.unscoped.enable = Some(true);
config.settings.unscoped.enable_paths =
Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]);
@@ -1330,8 +1311,10 @@ mod tests {
#[test]
fn test_set_workspace_settings_defaults() {
let mut config = Config::new();
- config
- .set_workspace_settings(serde_json::from_value(json!({})).unwrap(), None);
+ config.set_workspace_settings(
+ serde_json::from_value(json!({})).unwrap(),
+ vec![],
+ );
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings {
@@ -1465,7 +1448,7 @@ mod tests {
let mut config = Config::new();
config.set_workspace_settings(
serde_json::from_value(json!({ "cache": "" })).unwrap(),
- None,
+ vec![],
);
assert_eq!(
config.workspace_settings().clone(),
@@ -1478,7 +1461,7 @@ mod tests {
let mut config = Config::new();
config.set_workspace_settings(
serde_json::from_value(json!({ "import_map": "" })).unwrap(),
- None,
+ vec![],
);
assert_eq!(
config.workspace_settings().clone(),
@@ -1491,7 +1474,7 @@ mod tests {
let mut config = Config::new();
config.set_workspace_settings(
serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
- None,
+ vec![],
);
assert_eq!(
config.workspace_settings().clone(),
@@ -1504,7 +1487,7 @@ mod tests {
let mut config = Config::new();
config.set_workspace_settings(
serde_json::from_value(json!({ "config": "" })).unwrap(),
- None,
+ vec![],
);
assert_eq!(
config.workspace_settings().clone(),
@@ -1513,82 +1496,9 @@ mod tests {
}
#[test]
- fn config_get_enabled_paths() {
- let mut config = Config::new();
- config.workspace_folders = vec![
- (
- Url::parse("file:///root1/").unwrap(),
- lsp::WorkspaceFolder {
- uri: Url::parse("file:///root1/").unwrap(),
- name: "1".to_string(),
- },
- ),
- (
- Url::parse("file:///root2/").unwrap(),
- lsp::WorkspaceFolder {
- uri: Url::parse("file:///root2/").unwrap(),
- name: "2".to_string(),
- },
- ),
- (
- Url::parse("file:///root3/").unwrap(),
- lsp::WorkspaceFolder {
- uri: Url::parse("file:///root3/").unwrap(),
- name: "3".to_string(),
- },
- ),
- ];
- config.set_workspace_settings(
- Default::default(),
- Some(
- vec![
- (
- Url::parse("file:///root1/").unwrap(),
- WorkspaceSettings {
- enable_paths: Some(vec![
- "sub_dir".to_string(),
- "sub_dir/other".to_string(),
- "test.ts".to_string(),
- ]),
- ..Default::default()
- },
- ),
- (
- Url::parse("file:///root2/").unwrap(),
- WorkspaceSettings {
- enable_paths: Some(vec!["other.ts".to_string()]),
- ..Default::default()
- },
- ),
- (
- Url::parse("file:///root3/").unwrap(),
- WorkspaceSettings {
- enable: Some(true),
- ..Default::default()
- },
- ),
- ]
- .into_iter()
- .collect(),
- ),
- );
-
- assert_eq!(
- config.get_enabled_paths(),
- PathOrPatternSet::new(vec![
- PathOrPattern::Path(PathBuf::from("/root1/sub_dir")),
- PathOrPattern::Path(PathBuf::from("/root1/sub_dir/other")),
- PathOrPattern::Path(PathBuf::from("/root1/test.ts")),
- PathOrPattern::Path(PathBuf::from("/root2/other.ts")),
- PathOrPattern::Path(PathBuf::from("/root3/")),
- ])
- );
- }
-
- #[test]
fn config_enable_via_config_file_detection() {
let root_uri = resolve_url("file:///root/").unwrap();
- let mut config = Config::new_with_root(root_uri.clone());
+ let mut config = Config::new_with_roots(vec![root_uri.clone()]);
config.settings.unscoped.enable = None;
assert!(!config.specifier_enabled(&root_uri));
@@ -1602,7 +1512,7 @@ mod tests {
#[test]
fn config_specifier_enabled_matches_by_path_component() {
let root_uri = resolve_url("file:///root/").unwrap();
- let mut config = Config::new_with_root(root_uri.clone());
+ let mut config = Config::new_with_roots(vec![root_uri.clone()]);
config.settings.unscoped.enable_paths = Some(vec!["mo".to_string()]);
assert!(!config.specifier_enabled(&root_uri.join("mod.ts").unwrap()));
}
@@ -1610,7 +1520,7 @@ mod tests {
#[test]
fn config_specifier_enabled_for_test() {
let root_uri = resolve_url("file:///root/").unwrap();
- let mut config = Config::new_with_root(root_uri.clone());
+ let mut config = Config::new_with_roots(vec![root_uri.clone()]);
config.settings.unscoped.enable = Some(true);
config.settings.unscoped.enable_paths =
@@ -1693,7 +1603,7 @@ mod tests {
#[test]
fn config_snapshot_specifier_enabled_for_test() {
let root_uri = resolve_url("file:///root/").unwrap();
- let mut config = Config::new_with_root(root_uri.clone());
+ let mut config = Config::new_with_roots(vec![root_uri.clone()]);
config.settings.unscoped.enable = Some(true);
config.set_config_file(
ConfigFile::new(