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.rs163
1 files changed, 133 insertions, 30 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index f03388895..e445d34f0 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -17,6 +17,7 @@ use deno_ast::MediaType;
use deno_config::FmtOptionsConfig;
use deno_config::TsConfig;
use deno_core::anyhow::anyhow;
+use deno_core::normalize_path;
use deno_core::parking_lot::Mutex;
use deno_core::serde::de::DeserializeOwned;
use deno_core::serde::Deserialize;
@@ -31,6 +32,8 @@ use deno_npm::npm_rc::ResolvedNpmRc;
use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fs_util::specifier_to_file_path;
+use deno_semver::package::PackageNv;
+use deno_semver::Version;
use import_map::ImportMap;
use lsp::Url;
use lsp_types::ClientCapabilities;
@@ -1077,6 +1080,17 @@ impl LspTsConfig {
}
}
+#[derive(Debug, Clone)]
+pub struct LspWorkspaceConfig {
+ pub members: Vec<ModuleSpecifier>,
+}
+
+#[derive(Debug, Clone)]
+pub struct LspPackageConfig {
+ pub nv: PackageNv,
+ pub exports: Value,
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigWatchedFileType {
DenoJson,
@@ -1103,6 +1117,14 @@ pub struct ConfigData {
pub npmrc: Option<Arc<ResolvedNpmRc>>,
pub import_map: Option<Arc<ImportMap>>,
pub import_map_from_settings: bool,
+ pub package_config: Option<Arc<LspPackageConfig>>,
+ pub is_workspace_root: bool,
+ /// Workspace member directories. For a workspace root this will be a list of
+ /// members. For a member this will be the same list, representing self and
+ /// siblings. For a solitary package this will be `vec![self.scope]`. These
+ /// are the list of packages to override with local resolutions for this
+ /// config scope.
+ pub workspace_members: Arc<Vec<ModuleSpecifier>>,
watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>,
}
@@ -1110,7 +1132,7 @@ impl ConfigData {
async fn load(
config_file_specifier: Option<&ModuleSpecifier>,
scope: &ModuleSpecifier,
- parent: Option<(&ModuleSpecifier, &ConfigData)>,
+ workspace_root: Option<(&ModuleSpecifier, &ConfigData)>,
settings: &Settings,
file_fetcher: Option<&Arc<FileFetcher>>,
) -> Self {
@@ -1127,7 +1149,7 @@ impl ConfigData {
Self::load_inner(
Some(config_file),
scope,
- parent,
+ workspace_root,
settings,
file_fetcher,
)
@@ -1139,8 +1161,14 @@ impl ConfigData {
specifier.as_str(),
err
);
- let mut data =
- Self::load_inner(None, scope, parent, settings, file_fetcher).await;
+ let mut data = Self::load_inner(
+ None,
+ scope,
+ workspace_root,
+ settings,
+ file_fetcher,
+ )
+ .await;
data
.watched_files
.insert(specifier.clone(), ConfigWatchedFileType::DenoJson);
@@ -1158,14 +1186,15 @@ impl ConfigData {
}
}
} else {
- Self::load_inner(None, scope, parent, settings, file_fetcher).await
+ Self::load_inner(None, scope, workspace_root, settings, file_fetcher)
+ .await
}
}
async fn load_inner(
config_file: Option<ConfigFile>,
scope: &ModuleSpecifier,
- parent: Option<(&ModuleSpecifier, &ConfigData)>,
+ workspace_root: Option<(&ModuleSpecifier, &ConfigData)>,
settings: &Settings,
file_fetcher: Option<&Arc<FileFetcher>>,
) -> Self {
@@ -1190,12 +1219,12 @@ impl ConfigData {
}
let mut fmt_options = None;
- if let Some((_, parent_data)) = parent {
+ if let Some((_, workspace_data)) = workspace_root {
let has_own_fmt_options = config_file
.as_ref()
.is_some_and(|config_file| config_file.json.fmt.is_some());
if !has_own_fmt_options {
- fmt_options = Some(parent_data.fmt_options.clone())
+ fmt_options = Some(workspace_data.fmt_options.clone())
}
}
let fmt_options = fmt_options.unwrap_or_else(|| {
@@ -1221,14 +1250,14 @@ impl ConfigData {
});
let mut lint_options_rules = None;
- if let Some((_, parent_data)) = parent {
+ if let Some((_, workspace_data)) = workspace_root {
let has_own_lint_options = config_file
.as_ref()
.is_some_and(|config_file| config_file.json.lint.is_some());
if !has_own_lint_options {
lint_options_rules = Some((
- parent_data.lint_options.clone(),
- parent_data.lint_rules.clone(),
+ workspace_data.lint_options.clone(),
+ workspace_data.lint_rules.clone(),
))
}
}
@@ -1474,6 +1503,44 @@ impl ConfigData {
}
}
+ let package_config = config_file.as_ref().and_then(|c| {
+ Some(LspPackageConfig {
+ nv: PackageNv {
+ name: c.json.name.clone()?,
+ version: Version::parse_standard(c.json.version.as_ref()?).ok()?,
+ },
+ exports: c.json.exports.clone()?,
+ })
+ });
+
+ let is_workspace_root = config_file
+ .as_ref()
+ .is_some_and(|c| !c.json.workspaces.is_empty());
+ let workspace_members = if is_workspace_root {
+ Arc::new(
+ config_file
+ .as_ref()
+ .map(|c| {
+ c.json
+ .workspaces
+ .iter()
+ .flat_map(|p| {
+ let dir_specifier = c.specifier.join(p).ok()?;
+ let dir_path = specifier_to_file_path(&dir_specifier).ok()?;
+ Url::from_directory_path(normalize_path(dir_path)).ok()
+ })
+ .collect()
+ })
+ .unwrap_or_default(),
+ )
+ } else if let Some((_, workspace_data)) = workspace_root {
+ workspace_data.workspace_members.clone()
+ } else if config_file.as_ref().is_some_and(|c| c.json.name.is_some()) {
+ Arc::new(vec![scope.clone()])
+ } else {
+ Arc::new(vec![])
+ };
+
ConfigData {
scope: scope.clone(),
config_file: config_file.map(Arc::new),
@@ -1490,6 +1557,9 @@ impl ConfigData {
npmrc,
import_map: import_map.map(Arc::new),
import_map_from_settings,
+ package_config: package_config.map(Arc::new),
+ is_workspace_root,
+ workspace_members,
watched_files,
}
}
@@ -1639,27 +1709,57 @@ impl ConfigTree {
}
for specifier in workspace_files {
- if specifier.path().ends_with("/deno.json")
- || specifier.path().ends_with("/deno.jsonc")
+ if !(specifier.path().ends_with("/deno.json")
+ || specifier.path().ends_with("/deno.jsonc"))
{
- if let Ok(scope) = specifier.join(".") {
- if !scopes.contains_key(&scope) {
- let parent = scopes
- .iter()
- .rev()
- .find(|(s, _)| scope.as_str().starts_with(s.as_str()));
- let data = ConfigData::load(
- Some(specifier),
- &scope,
- parent,
- settings,
- Some(file_fetcher),
- )
- .await;
- scopes.insert(scope, data);
+ continue;
+ }
+ let Ok(scope) = specifier.join(".") else {
+ continue;
+ };
+ if scopes.contains_key(&scope) {
+ continue;
+ }
+ let data = ConfigData::load(
+ Some(specifier),
+ &scope,
+ None,
+ settings,
+ Some(file_fetcher),
+ )
+ .await;
+ if data.is_workspace_root {
+ for member_scope in data.workspace_members.iter() {
+ if scopes.contains_key(member_scope) {
+ continue;
}
+ let Ok(member_path) = specifier_to_file_path(member_scope) else {
+ continue;
+ };
+ let Some(config_file_path) = Some(member_path.join("deno.json"))
+ .filter(|p| p.exists())
+ .or_else(|| {
+ Some(member_path.join("deno.jsonc")).filter(|p| p.exists())
+ })
+ else {
+ continue;
+ };
+ let Ok(config_file_specifier) = Url::from_file_path(config_file_path)
+ else {
+ continue;
+ };
+ let member_data = ConfigData::load(
+ Some(&config_file_specifier),
+ member_scope,
+ Some((&scope, &data)),
+ settings,
+ Some(file_fetcher),
+ )
+ .await;
+ scopes.insert(member_scope.clone(), member_data);
}
}
+ scopes.insert(scope, data);
}
for folder_uri in settings.by_workspace_folder.keys() {
@@ -1741,8 +1841,11 @@ fn resolve_node_modules_dir(
fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option<Lockfile> {
match read_lockfile_at_path(lockfile_path) {
Ok(value) => {
- if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) {
- lsp_log!(" Resolved lockfile: \"{}\"", specifier);
+ if value.filename.exists() {
+ if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename)
+ {
+ lsp_log!(" Resolved lockfile: \"{}\"", specifier);
+ }
}
Some(value)
}