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.rs377
1 files changed, 306 insertions, 71 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index 7bbf90d21..1a83c00eb 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -5,8 +5,9 @@ use crate::args::ConfigFile;
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_core::error::AnyError;
+use deno_ast::MediaType;
use deno_core::parking_lot::Mutex;
+use deno_core::serde::de::DeserializeOwned;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde_json;
@@ -86,6 +87,13 @@ impl Default for CodeLensSpecifierSettings {
}
}
+#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct DenoCompletionSettings {
+ #[serde(default)]
+ pub imports: ImportCompletionSettings,
+}
+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CompletionSettings {
@@ -97,8 +105,6 @@ pub struct CompletionSettings {
pub paths: bool,
#[serde(default = "is_true")]
pub auto_imports: bool,
- #[serde(default)]
- pub imports: ImportCompletionSettings,
}
impl Default for CompletionSettings {
@@ -108,7 +114,6 @@ impl Default for CompletionSettings {
names: true,
paths: true,
auto_imports: true,
- imports: ImportCompletionSettings::default(),
}
}
}
@@ -281,6 +286,15 @@ fn empty_string_none<'de, D: serde::Deserializer<'de>>(
Ok(o.filter(|s| !s.is_empty()))
}
+#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct LanguageWorkspaceSettings {
+ #[serde(default)]
+ pub inlay_hints: InlayHintsSettings,
+ #[serde(default)]
+ pub suggest: CompletionSettings,
+}
+
/// Deno language server specific settings that are applied to a workspace.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
@@ -319,9 +333,6 @@ pub struct WorkspaceSettings {
#[serde(default)]
pub code_lens: CodeLensSettings,
- #[serde(default)]
- pub inlay_hints: InlayHintsSettings,
-
/// A flag that indicates if internal debug logging should be made available.
#[serde(default)]
pub internal_debug: bool,
@@ -334,10 +345,8 @@ pub struct WorkspaceSettings {
#[serde(default = "default_document_preload_limit")]
pub document_preload_limit: usize,
- /// A flag that indicates if Dene should validate code against the unstable
- /// APIs for the workspace.
#[serde(default)]
- pub suggest: CompletionSettings,
+ pub suggest: DenoCompletionSettings,
/// Testing settings for the workspace.
#[serde(default)]
@@ -355,6 +364,12 @@ pub struct WorkspaceSettings {
#[serde(default)]
pub unstable: bool,
+
+ #[serde(default)]
+ pub javascript: LanguageWorkspaceSettings,
+
+ #[serde(default)]
+ pub typescript: LanguageWorkspaceSettings,
}
impl Default for WorkspaceSettings {
@@ -368,7 +383,6 @@ impl Default for WorkspaceSettings {
config: None,
import_map: None,
code_lens: Default::default(),
- inlay_hints: Default::default(),
internal_debug: false,
lint: true,
document_preload_limit: default_document_preload_limit(),
@@ -377,28 +391,220 @@ impl Default for WorkspaceSettings {
tls_certificate: None,
unsafely_ignore_certificate_errors: None,
unstable: false,
+ javascript: Default::default(),
+ typescript: Default::default(),
}
}
}
impl WorkspaceSettings {
+ pub fn from_raw_settings(
+ deno: Value,
+ javascript: Value,
+ typescript: Value,
+ ) -> Self {
+ fn parse_or_default<T: Default + DeserializeOwned>(
+ value: Value,
+ description: &str,
+ ) -> T {
+ if value.is_null() {
+ return T::default();
+ }
+ match serde_json::from_value(value) {
+ Ok(v) => v,
+ Err(err) => {
+ lsp_warn!("Couldn't parse {description}: {err}");
+ T::default()
+ }
+ }
+ }
+ let deno_inlay_hints =
+ deno.as_object().and_then(|o| o.get("inlayHints").cloned());
+ let deno_suggest = deno.as_object().and_then(|o| o.get("suggest").cloned());
+ let mut settings: Self = parse_or_default(deno, "settings under \"deno\"");
+ settings.javascript =
+ parse_or_default(javascript, "settings under \"javascript\"");
+ settings.typescript =
+ parse_or_default(typescript, "settings under \"typescript\"");
+ if let Some(inlay_hints) = deno_inlay_hints {
+ let inlay_hints: InlayHintsSettings =
+ parse_or_default(inlay_hints, "settings under \"deno.inlayHints\"");
+ if inlay_hints.parameter_names.enabled != Default::default() {
+ lsp_warn!("\"deno.inlayHints.parameterNames.enabled\" is deprecated. Instead use \"javascript.inlayHints.parameterNames.enabled\" and \"typescript.inlayHints.parameterNames.enabled\".");
+ settings.javascript.inlay_hints.parameter_names.enabled =
+ inlay_hints.parameter_names.enabled.clone();
+ settings.typescript.inlay_hints.parameter_names.enabled =
+ inlay_hints.parameter_names.enabled;
+ }
+ if !inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name
+ {
+ lsp_warn!("\"deno.inlayHints.parameterNames.suppressWhenArgumentMatchesName\" is deprecated. Instead use \"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName\" and \"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName\".");
+ settings
+ .javascript
+ .inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name = inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name;
+ settings
+ .typescript
+ .inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name = inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name;
+ }
+ if inlay_hints.parameter_types.enabled {
+ lsp_warn!("\"deno.inlayHints.parameterTypes.enabled\" is deprecated. Instead use \"javascript.inlayHints.parameterTypes.enabled\" and \"typescript.inlayHints.parameterTypes.enabled\".");
+ settings.javascript.inlay_hints.parameter_types.enabled =
+ inlay_hints.parameter_types.enabled;
+ settings.typescript.inlay_hints.parameter_types.enabled =
+ inlay_hints.parameter_types.enabled;
+ }
+ if inlay_hints.variable_types.enabled {
+ lsp_warn!("\"deno.inlayHints.variableTypes.enabled\" is deprecated. Instead use \"javascript.inlayHints.variableTypes.enabled\" and \"typescript.inlayHints.variableTypes.enabled\".");
+ settings.javascript.inlay_hints.variable_types.enabled =
+ inlay_hints.variable_types.enabled;
+ settings.typescript.inlay_hints.variable_types.enabled =
+ inlay_hints.variable_types.enabled;
+ }
+ if !inlay_hints.variable_types.suppress_when_type_matches_name {
+ lsp_warn!("\"deno.inlayHints.variableTypes.suppressWhenTypeMatchesName\" is deprecated. Instead use \"javascript.inlayHints.variableTypes.suppressWhenTypeMatchesName\" and \"typescript.inlayHints.variableTypes.suppressWhenTypeMatchesName\".");
+ settings
+ .javascript
+ .inlay_hints
+ .variable_types
+ .suppress_when_type_matches_name =
+ inlay_hints.variable_types.suppress_when_type_matches_name;
+ settings
+ .typescript
+ .inlay_hints
+ .variable_types
+ .suppress_when_type_matches_name =
+ inlay_hints.variable_types.suppress_when_type_matches_name;
+ }
+ if inlay_hints.property_declaration_types.enabled {
+ lsp_warn!("\"deno.inlayHints.propertyDeclarationTypes.enabled\" is deprecated. Instead use \"javascript.inlayHints.propertyDeclarationTypes.enabled\" and \"typescript.inlayHints.propertyDeclarationTypes.enabled\".");
+ settings
+ .javascript
+ .inlay_hints
+ .property_declaration_types
+ .enabled = inlay_hints.property_declaration_types.enabled;
+ settings
+ .typescript
+ .inlay_hints
+ .property_declaration_types
+ .enabled = inlay_hints.property_declaration_types.enabled;
+ }
+ if inlay_hints.function_like_return_types.enabled {
+ lsp_warn!("\"deno.inlayHints.functionLikeReturnTypes.enabled\" is deprecated. Instead use \"javascript.inlayHints.functionLikeReturnTypes.enabled\" and \"typescript.inlayHints.functionLikeReturnTypes.enabled\".");
+ settings
+ .javascript
+ .inlay_hints
+ .function_like_return_types
+ .enabled = inlay_hints.function_like_return_types.enabled;
+ settings
+ .typescript
+ .inlay_hints
+ .function_like_return_types
+ .enabled = inlay_hints.function_like_return_types.enabled;
+ }
+ if inlay_hints.enum_member_values.enabled {
+ lsp_warn!("\"deno.inlayHints.enumMemberValues.enabled\" is deprecated. Instead use \"javascript.inlayHints.enumMemberValues.enabled\" and \"typescript.inlayHints.enumMemberValues.enabled\".");
+ settings.javascript.inlay_hints.enum_member_values.enabled =
+ inlay_hints.enum_member_values.enabled;
+ settings.typescript.inlay_hints.enum_member_values.enabled =
+ inlay_hints.enum_member_values.enabled;
+ }
+ }
+ if let Some(suggest) = deno_suggest {
+ let suggest: CompletionSettings =
+ parse_or_default(suggest, "settings under \"deno.suggest\"");
+ if suggest.complete_function_calls {
+ lsp_warn!("\"deno.suggest.completeFunctionCalls\" is deprecated. Instead use \"javascript.suggest.completeFunctionCalls\" and \"typescript.suggest.completeFunctionCalls\".");
+ settings.javascript.suggest.complete_function_calls =
+ suggest.complete_function_calls;
+ settings.typescript.suggest.complete_function_calls =
+ suggest.complete_function_calls;
+ }
+ if !suggest.names {
+ lsp_warn!("\"deno.suggest.names\" is deprecated. Instead use \"javascript.suggest.names\" and \"typescript.suggest.names\".");
+ settings.javascript.suggest.names = suggest.names;
+ settings.typescript.suggest.names = suggest.names;
+ }
+ if !suggest.paths {
+ lsp_warn!("\"deno.suggest.paths\" is deprecated. Instead use \"javascript.suggest.paths\" and \"typescript.suggest.paths\".");
+ settings.javascript.suggest.paths = suggest.paths;
+ settings.typescript.suggest.paths = suggest.paths;
+ }
+ if !suggest.auto_imports {
+ lsp_warn!("\"deno.suggest.autoImports\" is deprecated. Instead use \"javascript.suggest.autoImports\" and \"typescript.suggest.autoImports\".");
+ settings.javascript.suggest.auto_imports = suggest.auto_imports;
+ settings.typescript.suggest.auto_imports = suggest.auto_imports;
+ }
+ }
+ settings
+ }
+
+ pub fn from_initialization_options(options: Value) -> Self {
+ let deno = options;
+ let javascript = deno
+ .as_object()
+ .and_then(|o| o.get("javascript").cloned())
+ .unwrap_or_default();
+ let typescript = deno
+ .as_object()
+ .and_then(|o| o.get("typescript").cloned())
+ .unwrap_or_default();
+ Self::from_raw_settings(deno, javascript, typescript)
+ }
+
/// Determine if any code lenses are enabled at all. This allows short
/// circuiting when there are no code lenses enabled.
pub fn enabled_code_lens(&self) -> bool {
self.code_lens.implementations || self.code_lens.references
}
+ pub fn language_settings_for_specifier(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<&LanguageWorkspaceSettings> {
+ match MediaType::from_specifier(specifier) {
+ MediaType::JavaScript
+ | MediaType::Jsx
+ | MediaType::Mjs
+ | MediaType::Cjs => Some(&self.javascript),
+ MediaType::TypeScript
+ | MediaType::Mts
+ | MediaType::Cts
+ | MediaType::Dts
+ | MediaType::Dmts
+ | MediaType::Dcts
+ | MediaType::Tsx => Some(&self.typescript),
+ MediaType::Json
+ | MediaType::Wasm
+ | MediaType::TsBuildInfo
+ | MediaType::SourceMap
+ | MediaType::Unknown => None,
+ }
+ }
+
/// Determine if any inlay hints are enabled. This allows short circuiting
/// when there are no inlay hints enabled.
- pub fn enabled_inlay_hints(&self) -> bool {
+ pub fn enabled_inlay_hints(&self, specifier: &ModuleSpecifier) -> bool {
+ let Some(settings) = self.language_settings_for_specifier(specifier) else {
+ return false;
+ };
!matches!(
- self.inlay_hints.parameter_names.enabled,
+ settings.inlay_hints.parameter_names.enabled,
InlayHintsParamNamesEnabled::None
- ) || self.inlay_hints.parameter_types.enabled
- || self.inlay_hints.variable_types.enabled
- || self.inlay_hints.property_declaration_types.enabled
- || self.inlay_hints.function_like_return_types.enabled
- || self.inlay_hints.enum_member_values.enabled
+ ) || settings.inlay_hints.parameter_types.enabled
+ || settings.inlay_hints.variable_types.enabled
+ || settings.inlay_hints.property_declaration_types.enabled
+ || settings.inlay_hints.function_like_return_types.enabled
+ || settings.inlay_hints.enum_member_values.enabled
}
}
@@ -593,16 +799,12 @@ impl Config {
/// Set the workspace settings directly, which occurs during initialization
/// and when the client does not support workspace configuration requests
- pub fn set_workspace_settings(
- &mut self,
- value: Value,
- ) -> Result<(), AnyError> {
- self.settings.workspace = serde_json::from_value(value)?;
+ pub fn set_workspace_settings(&mut self, settings: WorkspaceSettings) {
+ self.settings.workspace = settings;
// See https://github.com/denoland/vscode_deno/issues/908.
if self.settings.workspace.enable_paths == Some(vec![]) {
self.settings.workspace.enable_paths = None;
}
- Ok(())
}
pub fn snapshot(&self) -> Arc<ConfigSnapshot> {
@@ -912,6 +1114,7 @@ fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option<Lockfile> {
mod tests {
use super::*;
use deno_core::resolve_url;
+ use deno_core::serde_json;
use deno_core::serde_json::json;
use pretty_assertions::assert_eq;
@@ -921,11 +1124,12 @@ mod tests {
let mut config = Config::new_with_root(root_uri);
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
- config
- .set_workspace_settings(json!({
+ config.set_workspace_settings(
+ serde_json::from_value(json!({
"enable": true
}))
- .expect("could not update");
+ .unwrap(),
+ );
assert!(config.specifier_enabled(&specifier));
}
@@ -935,11 +1139,12 @@ mod tests {
let mut config = Config::new_with_root(root_uri);
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
- config
- .set_workspace_settings(json!({
+ config.set_workspace_settings(
+ serde_json::from_value(json!({
"enable": true
}))
- .expect("could not update");
+ .unwrap(),
+ );
let config_snapshot = config.snapshot();
assert!(config_snapshot.specifier_enabled(&specifier));
}
@@ -954,7 +1159,7 @@ mod tests {
assert!(!config.specifier_enabled(&specifier_b));
let workspace_settings =
serde_json::from_str(r#"{ "enablePaths": ["worker"] }"#).unwrap();
- config.set_workspace_settings(workspace_settings).unwrap();
+ config.set_workspace_settings(workspace_settings);
assert!(config.specifier_enabled(&specifier_a));
assert!(!config.specifier_enabled(&specifier_b));
let config_snapshot = config.snapshot();
@@ -979,9 +1184,7 @@ mod tests {
#[test]
fn test_set_workspace_settings_defaults() {
let mut config = Config::new();
- config
- .set_workspace_settings(json!({}))
- .expect("could not update");
+ config.set_workspace_settings(serde_json::from_value(json!({})).unwrap());
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings {
@@ -998,34 +1201,10 @@ mod tests {
references_all_functions: false,
test: true,
},
- inlay_hints: InlayHintsSettings {
- parameter_names: InlayHintsParamNamesOptions {
- enabled: InlayHintsParamNamesEnabled::None,
- suppress_when_argument_matches_name: true
- },
- parameter_types: InlayHintsParamTypesOptions { enabled: false },
- variable_types: InlayHintsVarTypesOptions {
- enabled: false,
- suppress_when_type_matches_name: true
- },
- property_declaration_types: InlayHintsPropDeclTypesOptions {
- enabled: false
- },
- function_like_return_types: InlayHintsFuncLikeReturnTypesOptions {
- enabled: false
- },
- enum_member_values: InlayHintsEnumMemberValuesOptions {
- enabled: false
- },
- },
internal_debug: false,
lint: true,
document_preload_limit: 1_000,
- suggest: CompletionSettings {
- complete_function_calls: false,
- names: true,
- paths: true,
- auto_imports: true,
+ suggest: DenoCompletionSettings {
imports: ImportCompletionSettings {
auto_discover: true,
hosts: HashMap::new(),
@@ -1037,6 +1216,62 @@ mod tests {
tls_certificate: None,
unsafely_ignore_certificate_errors: None,
unstable: false,
+ javascript: LanguageWorkspaceSettings {
+ inlay_hints: InlayHintsSettings {
+ parameter_names: InlayHintsParamNamesOptions {
+ enabled: InlayHintsParamNamesEnabled::None,
+ suppress_when_argument_matches_name: true
+ },
+ parameter_types: InlayHintsParamTypesOptions { enabled: false },
+ variable_types: InlayHintsVarTypesOptions {
+ enabled: false,
+ suppress_when_type_matches_name: true
+ },
+ property_declaration_types: InlayHintsPropDeclTypesOptions {
+ enabled: false
+ },
+ function_like_return_types: InlayHintsFuncLikeReturnTypesOptions {
+ enabled: false
+ },
+ enum_member_values: InlayHintsEnumMemberValuesOptions {
+ enabled: false
+ },
+ },
+ suggest: CompletionSettings {
+ complete_function_calls: false,
+ names: true,
+ paths: true,
+ auto_imports: true,
+ },
+ },
+ typescript: LanguageWorkspaceSettings {
+ inlay_hints: InlayHintsSettings {
+ parameter_names: InlayHintsParamNamesOptions {
+ enabled: InlayHintsParamNamesEnabled::None,
+ suppress_when_argument_matches_name: true
+ },
+ parameter_types: InlayHintsParamTypesOptions { enabled: false },
+ variable_types: InlayHintsVarTypesOptions {
+ enabled: false,
+ suppress_when_type_matches_name: true
+ },
+ property_declaration_types: InlayHintsPropDeclTypesOptions {
+ enabled: false
+ },
+ function_like_return_types: InlayHintsFuncLikeReturnTypesOptions {
+ enabled: false
+ },
+ enum_member_values: InlayHintsEnumMemberValuesOptions {
+ enabled: false
+ },
+ },
+ suggest: CompletionSettings {
+ complete_function_calls: false,
+ names: true,
+ paths: true,
+ auto_imports: true,
+ },
+ },
}
);
}
@@ -1044,9 +1279,9 @@ mod tests {
#[test]
fn test_empty_cache() {
let mut config = Config::new();
- config
- .set_workspace_settings(json!({ "cache": "" }))
- .expect("could not update");
+ config.set_workspace_settings(
+ serde_json::from_value(json!({ "cache": "" })).unwrap(),
+ );
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings::default()
@@ -1056,9 +1291,9 @@ mod tests {
#[test]
fn test_empty_import_map() {
let mut config = Config::new();
- config
- .set_workspace_settings(json!({ "import_map": "" }))
- .expect("could not update");
+ config.set_workspace_settings(
+ serde_json::from_value(json!({ "import_map": "" })).unwrap(),
+ );
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings::default()
@@ -1068,9 +1303,9 @@ mod tests {
#[test]
fn test_empty_tls_certificate() {
let mut config = Config::new();
- config
- .set_workspace_settings(json!({ "tls_certificate": "" }))
- .expect("could not update");
+ config.set_workspace_settings(
+ serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
+ );
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings::default()
@@ -1080,9 +1315,9 @@ mod tests {
#[test]
fn test_empty_config() {
let mut config = Config::new();
- config
- .set_workspace_settings(json!({ "config": "" }))
- .expect("could not update");
+ config.set_workspace_settings(
+ serde_json::from_value(json!({ "config": "" })).unwrap(),
+ );
assert_eq!(
config.workspace_settings().clone(),
WorkspaceSettings::default()