summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2023-09-21 06:46:39 +0100
committerGitHub <noreply@github.com>2023-09-21 06:46:39 +0100
commita4ac6a3f5f1b02b5290ab603d76b5cd9030731ca (patch)
tree6abbd056b42355f818383393cebe571072e0253b
parent0981aefbdce4c0be961f6447bf68069c518227ef (diff)
refactor(lsp): store language sections in WorkspaceSettings (#20593)
When sending configuration requests to the client, reads `javascript` and `typescript` sections in addition to `deno`. The LSP's initialization options now accepts `javascript` and `typescript` namespaces.
-rw-r--r--cli/lsp/client.rs51
-rw-r--r--cli/lsp/config.rs377
-rw-r--r--cli/lsp/language_server.rs81
-rw-r--r--cli/lsp/repl.rs27
-rw-r--r--cli/lsp/tsc.rs65
-rw-r--r--cli/tests/integration/lsp_tests.rs62
-rw-r--r--test_util/src/lsp.rs23
7 files changed, 501 insertions, 185 deletions
diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs
index acef59f97..b5cdf8eb9 100644
--- a/cli/lsp/client.rs
+++ b/cli/lsp/client.rs
@@ -7,7 +7,6 @@ use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::serde_json;
-use deno_core::serde_json::Value;
use deno_core::unsync::spawn;
use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::ConfigurationItem;
@@ -15,6 +14,7 @@ use tower_lsp::lsp_types::ConfigurationItem;
use crate::lsp::repl::get_repl_workspace_settings;
use super::config::SpecifierSettings;
+use super::config::WorkspaceSettings;
use super::config::SETTINGS_SECTION;
use super::lsp_custom;
use super::testing::lsp_custom as testing_lsp_custom;
@@ -148,7 +148,9 @@ impl OutsideLockClient {
}
}
- pub async fn workspace_configuration(&self) -> Result<Value, AnyError> {
+ pub async fn workspace_configuration(
+ &self,
+ ) -> Result<WorkspaceSettings, AnyError> {
self.0.workspace_configuration().await
}
@@ -186,7 +188,9 @@ trait ClientTrait: Send + Sync {
&self,
uris: Vec<lsp::Url>,
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError>;
- async fn workspace_configuration(&self) -> Result<Value, AnyError>;
+ async fn workspace_configuration(
+ &self,
+ ) -> Result<WorkspaceSettings, AnyError>;
async fn show_message(&self, message_type: lsp::MessageType, text: String);
async fn register_capability(
&self,
@@ -284,19 +288,36 @@ impl ClientTrait for TowerClient {
)
}
- async fn workspace_configuration(&self) -> Result<Value, AnyError> {
+ async fn workspace_configuration(
+ &self,
+ ) -> Result<WorkspaceSettings, AnyError> {
let config_response = self
.0
- .configuration(vec![ConfigurationItem {
- scope_uri: None,
- section: Some(SETTINGS_SECTION.to_string()),
- }])
+ .configuration(vec![
+ ConfigurationItem {
+ scope_uri: None,
+ section: Some(SETTINGS_SECTION.to_string()),
+ },
+ ConfigurationItem {
+ scope_uri: None,
+ section: Some("javascript".to_string()),
+ },
+ ConfigurationItem {
+ scope_uri: None,
+ section: Some("typescript".to_string()),
+ },
+ ])
.await;
match config_response {
- Ok(value_vec) => match value_vec.get(0).cloned() {
- Some(value) => Ok(value),
- None => bail!("Missing response workspace configuration."),
- },
+ Ok(configs) => {
+ let mut configs = configs.into_iter();
+ let deno = serde_json::to_value(configs.next()).unwrap();
+ let javascript = serde_json::to_value(configs.next()).unwrap();
+ let typescript = serde_json::to_value(configs.next()).unwrap();
+ Ok(WorkspaceSettings::from_raw_settings(
+ deno, javascript, typescript,
+ ))
+ }
Err(err) => {
bail!("Error getting workspace configuration: {}", err)
}
@@ -367,8 +388,10 @@ impl ClientTrait for ReplClient {
Ok(settings)
}
- async fn workspace_configuration(&self) -> Result<Value, AnyError> {
- Ok(serde_json::to_value(get_repl_workspace_settings()).unwrap())
+ async fn workspace_configuration(
+ &self,
+ ) -> Result<WorkspaceSettings, AnyError> {
+ Ok(get_repl_workspace_settings())
}
async fn show_message(
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()
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 8ef7d124c..be3d7f5af 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -45,6 +45,7 @@ use super::code_lens;
use super::completions;
use super::config::Config;
use super::config::ConfigSnapshot;
+use super::config::WorkspaceSettings;
use super::config::SETTINGS_SECTION;
use super::diagnostics;
use super::diagnostics::DiagnosticServerUpdateMessage;
@@ -1265,11 +1266,10 @@ impl Inner {
}
{
- if let Some(value) = params.initialization_options {
- self.config.set_workspace_settings(value).map_err(|err| {
- error!("Cannot set workspace settings: {}", err);
- LspError::internal_error()
- })?;
+ if let Some(options) = params.initialization_options {
+ self.config.set_workspace_settings(
+ WorkspaceSettings::from_initialization_options(options),
+ );
}
if let Some(folders) = params.workspace_folders {
self.config.workspace_folders = folders
@@ -1472,24 +1472,26 @@ impl Inner {
async fn did_change_configuration(
&mut self,
- client_workspace_config: Option<Value>,
+ client_workspace_config: Option<WorkspaceSettings>,
params: DidChangeConfigurationParams,
) {
let maybe_config =
if self.config.client_capabilities.workspace_configuration {
client_workspace_config
} else {
- params
- .settings
- .as_object()
- .and_then(|settings| settings.get(SETTINGS_SECTION))
- .cloned()
+ params.settings.as_object().map(|settings| {
+ let deno =
+ serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
+ let javascript =
+ serde_json::to_value(settings.get("javascript")).unwrap();
+ let typescript =
+ serde_json::to_value(settings.get("typescript")).unwrap();
+ WorkspaceSettings::from_raw_settings(deno, javascript, typescript)
+ })
};
- if let Some(value) = maybe_config {
- if let Err(err) = self.config.set_workspace_settings(value) {
- error!("failed to update settings: {}", err);
- }
+ if let Some(settings) = maybe_config {
+ self.config.set_workspace_settings(settings);
}
self.update_debug_flag();
@@ -1929,7 +1931,10 @@ impl Inner {
(&self.fmt_options.options).into(),
tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()),
- ..self.config.workspace_settings().into()
+ ..tsc::UserPreferences::from_workspace_settings_for_specifier(
+ self.config.workspace_settings(),
+ &specifier,
+ )
},
)
.await;
@@ -1990,7 +1995,10 @@ impl Inner {
..line_index.offset_tsc(params.range.end)?,
Some(tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()),
- ..self.config.workspace_settings().into()
+ ..tsc::UserPreferences::from_workspace_settings_for_specifier(
+ self.config.workspace_settings(),
+ &specifier,
+ )
}),
only,
)
@@ -2049,7 +2057,10 @@ impl Inner {
(&self.fmt_options.options).into(),
tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()),
- ..self.config.workspace_settings().into()
+ ..tsc::UserPreferences::from_workspace_settings_for_specifier(
+ self.config.workspace_settings(),
+ &code_action_data.specifier,
+ )
},
)
.await?;
@@ -2090,7 +2101,7 @@ impl Inner {
.ts_server
.get_edits_for_refactor(
self.snapshot(),
- action_data.specifier,
+ action_data.specifier.clone(),
(&self.fmt_options.options).into(),
line_index.offset_tsc(action_data.range.start)?
..line_index.offset_tsc(action_data.range.end)?,
@@ -2098,7 +2109,10 @@ impl Inner {
action_data.action_name,
Some(tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()),
- ..self.config.workspace_settings().into()
+ ..tsc::UserPreferences::from_workspace_settings_for_specifier(
+ self.config.workspace_settings(),
+ &action_data.specifier,
+ )
}),
)
.await?;
@@ -2425,9 +2439,11 @@ impl Inner {
),
include_automatic_optional_chain_completions: Some(true),
include_completions_for_import_statements: Some(true),
- include_completions_for_module_exports: Some(
- self.config.workspace_settings().suggest.auto_imports,
- ),
+ include_completions_for_module_exports: self
+ .config
+ .workspace_settings()
+ .language_settings_for_specifier(&specifier)
+ .map(|s| s.suggest.auto_imports),
include_completions_with_object_literal_method_snippets: Some(
use_snippets,
),
@@ -2454,7 +2470,13 @@ impl Inner {
if let Some(completions) = maybe_completion_info {
let results = completions.as_completion_response(
line_index,
- &self.config.workspace_settings().suggest,
+ &self
+ .config
+ .workspace_settings()
+ .language_settings_for_specifier(&specifier)
+ .cloned()
+ .unwrap_or_default()
+ .suggest,
&specifier,
position,
self,
@@ -3284,7 +3306,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
.workspace_configuration()
.await;
match config_response {
- Ok(value) => Some(value),
+ Ok(settings) => Some(settings),
Err(err) => {
error!("{}", err);
None
@@ -3601,7 +3623,7 @@ impl Inner {
let workspace_settings = self.config.workspace_settings();
if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier)
- || !workspace_settings.enabled_inlay_hints()
+ || !workspace_settings.enabled_inlay_hints(&specifier)
{
return Ok(None);
}
@@ -3620,11 +3642,14 @@ impl Inner {
.ts_server
.provide_inlay_hints(
self.snapshot(),
- specifier,
+ specifier.clone(),
text_span,
tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()),
- ..workspace_settings.into()
+ ..tsc::UserPreferences::from_workspace_settings_for_specifier(
+ workspace_settings,
+ &specifier,
+ )
},
)
.await?;
diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs
index c09a142d6..5e1bb66de 100644
--- a/cli/lsp/repl.rs
+++ b/cli/lsp/repl.rs
@@ -33,7 +33,9 @@ use tower_lsp::LanguageServer;
use super::client::Client;
use super::config::CompletionSettings;
+use super::config::DenoCompletionSettings;
use super::config::ImportCompletionSettings;
+use super::config::LanguageWorkspaceSettings;
use super::config::TestingSettings;
use super::config::WorkspaceSettings;
@@ -292,23 +294,36 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
cache: None,
import_map: None,
code_lens: Default::default(),
- inlay_hints: Default::default(),
internal_debug: false,
lint: false,
document_preload_limit: 0, // don't pre-load any modules as it's expensive and not useful for the repl
tls_certificate: None,
unsafely_ignore_certificate_errors: None,
unstable: false,
- suggest: CompletionSettings {
- complete_function_calls: false,
- names: false,
- paths: false,
- auto_imports: false,
+ suggest: DenoCompletionSettings {
imports: ImportCompletionSettings {
auto_discover: false,
hosts: HashMap::from([("https://deno.land".to_string(), true)]),
},
},
testing: TestingSettings { args: vec![] },
+ javascript: LanguageWorkspaceSettings {
+ inlay_hints: Default::default(),
+ suggest: CompletionSettings {
+ complete_function_calls: false,
+ names: false,
+ paths: false,
+ auto_imports: false,
+ },
+ },
+ typescript: LanguageWorkspaceSettings {
+ inlay_hints: Default::default(),
+ suggest: CompletionSettings {
+ complete_function_calls: false,
+ names: false,
+ paths: false,
+ auto_imports: false,
+ },
+ },
}
}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 6ed0cf138..e4c71e976 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -3665,36 +3665,35 @@ pub struct UserPreferences {
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
}
-impl From<&config::WorkspaceSettings> for UserPreferences {
- fn from(workspace_settings: &config::WorkspaceSettings) -> Self {
- let inlay_hints = &workspace_settings.inlay_hints;
+impl UserPreferences {
+ pub fn from_workspace_settings_for_specifier(
+ settings: &config::WorkspaceSettings,
+ specifier: &ModuleSpecifier,
+ ) -> Self {
+ let language_settings = settings.language_settings_for_specifier(specifier);
Self {
- include_inlay_parameter_name_hints: Some(
- (&inlay_hints.parameter_names.enabled).into(),
- ),
- include_inlay_parameter_name_hints_when_argument_matches_name: Some(
- !inlay_hints
- .parameter_names
- .suppress_when_argument_matches_name,
- ),
- include_inlay_function_parameter_type_hints: Some(
- inlay_hints.parameter_types.enabled,
- ),
- include_inlay_variable_type_hints: Some(
- inlay_hints.variable_types.enabled,
- ),
- include_inlay_variable_type_hints_when_type_matches_name: Some(
- !inlay_hints.variable_types.suppress_when_type_matches_name,
- ),
- include_inlay_property_declaration_type_hints: Some(
- inlay_hints.property_declaration_types.enabled,
- ),
- include_inlay_function_like_return_type_hints: Some(
- inlay_hints.function_like_return_types.enabled,
- ),
- include_inlay_enum_member_value_hints: Some(
- inlay_hints.enum_member_values.enabled,
- ),
+ include_inlay_parameter_name_hints: language_settings
+ .map(|s| (&s.inlay_hints.parameter_names.enabled).into()),
+ include_inlay_parameter_name_hints_when_argument_matches_name:
+ language_settings.map(|s| {
+ !s.inlay_hints
+ .parameter_names
+ .suppress_when_argument_matches_name
+ }),
+ include_inlay_function_parameter_type_hints: language_settings
+ .map(|s| s.inlay_hints.parameter_types.enabled),
+ include_inlay_variable_type_hints: language_settings
+ .map(|s| s.inlay_hints.variable_types.enabled),
+ include_inlay_variable_type_hints_when_type_matches_name:
+ language_settings.map(|s| {
+ !s.inlay_hints.variable_types.suppress_when_type_matches_name
+ }),
+ include_inlay_property_declaration_type_hints: language_settings
+ .map(|s| s.inlay_hints.property_declaration_types.enabled),
+ include_inlay_function_like_return_type_hints: language_settings
+ .map(|s| s.inlay_hints.function_like_return_types.enabled),
+ include_inlay_enum_member_value_hints: language_settings
+ .map(|s| s.inlay_hints.enum_member_values.enabled),
..Default::default()
}
}
@@ -5153,14 +5152,20 @@ mod tests {
fn include_suppress_inlay_hit_settings() {
let mut settings = WorkspaceSettings::default();
settings
+ .typescript
.inlay_hints
.parameter_names
.suppress_when_argument_matches_name = true;
settings
+ .typescript
.inlay_hints
.variable_types
.suppress_when_type_matches_name = true;
- let user_preferences: UserPreferences = (&settings).into();
+ let user_preferences =
+ UserPreferences::from_workspace_settings_for_specifier(
+ &settings,
+ &ModuleSpecifier::parse("file:///foo.ts").unwrap(),
+ );
assert_eq!(
user_preferences.include_inlay_variable_type_hints_when_type_matches_name,
Some(false)
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index c211bbae4..4bc4713c6 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -776,12 +776,12 @@ fn lsp_import_attributes() {
"text": "{\"a\":1}"
}
}),
- json!([{
+ &json!({ "deno": {
"enable": true,
"codeLens": {
"test": true
}
- }]),
+ } }),
);
let diagnostics = client.did_open(json!({
@@ -1131,7 +1131,7 @@ fn lsp_hover_disabled() {
"text": "console.log(Date.now());\n"
}
}),
- json!([{ "enable": false }]),
+ &json!({ "deno": { "enable": false } }),
);
let res = client.write_request(
@@ -1329,11 +1329,11 @@ fn lsp_workspace_disable_enable_paths() {
}])
.set_deno_enable(false);
},
- json!([{
+ json!({ "deno": {
"enable": false,
"disablePaths": ["./worker/node.ts"],
"enablePaths": ["./worker"],
- }]),
+ } }),
);
client.did_open(json!({
@@ -3552,12 +3552,12 @@ fn lsp_code_lens_test_disabled() {
}
}),
// disable test code lens
- json!([{
+ &json!({ "deno": {
"enable": true,
"codeLens": {
"test": false
}
- }]),
+ } }),
);
let res = client.write_request(
"textDocument/codeLens",
@@ -5160,7 +5160,7 @@ fn lsp_code_actions_deadlock() {
"text": large_file_text,
}
}));
- client.handle_configuration_request(json!([{ "enable": true }]));
+ client.handle_configuration_request(&json!({ "deno": { "enable": true } }));
client.write_request(
"textDocument/semanticTokens/full",
json!({
@@ -5796,9 +5796,9 @@ fn lsp_semantic_tokens_for_disabled_module() {
|builder| {
builder.set_deno_enable(false);
},
- json!({
+ json!({ "deno": {
"enable": false
- }),
+ } }),
);
client.did_open(json!({
"textDocument": {
@@ -8096,7 +8096,7 @@ fn lsp_configuration_did_change() {
"settings": {}
}),
);
- let request = json!([{
+ let settings = json!({ "deno": {
"enable": true,
"codeLens": {
"implementations": true,
@@ -8116,11 +8116,11 @@ fn lsp_configuration_did_change() {
}
},
"unstable": false
- }]);
+ } });
// one for the workspace
- client.handle_configuration_request(request.clone());
+ client.handle_configuration_request(&settings);
// one for the specifier
- client.handle_configuration_request(request);
+ client.handle_configuration_request(&settings);
let list = client.get_completion_list(
"file:///a/file.ts",
@@ -8192,16 +8192,20 @@ fn lsp_completions_complete_function_calls() {
"settings": {}
}),
);
- let request = json!([{
- "enable": true,
- "suggest": {
- "completeFunctionCalls": true,
+ let settings = json!({
+ "deno": {
+ "enable": true,
+ },
+ "typescript": {
+ "suggest": {
+ "completeFunctionCalls": true,
+ },
},
- }]);
+ });
// one for the workspace
- client.handle_configuration_request(request.clone());
+ client.handle_configuration_request(&settings);
// one for the specifier
- client.handle_configuration_request(request);
+ client.handle_configuration_request(&settings);
let list = client.get_completion_list(
"file:///a/file.ts",
@@ -9304,7 +9308,7 @@ fn lsp_node_modules_dir() {
}),
);
- let request = json!([{
+ let settings = json!({ "deno": {
"enable": true,
"config": "./deno.json",
"codeLens": {
@@ -9321,11 +9325,11 @@ fn lsp_node_modules_dir() {
"imports": {}
},
"unstable": false
- }]);
+ } });
// one for the workspace
- client.handle_configuration_request(request.clone());
+ client.handle_configuration_request(&settings);
// one for the specifier
- client.handle_configuration_request(request);
+ client.handle_configuration_request(&settings);
};
refresh_config(&mut client);
@@ -9439,7 +9443,7 @@ fn lsp_vendor_dir() {
}),
);
- let request = json!([{
+ let settings = json!({ "deno": {
"enable": true,
"config": "./deno.json",
"codeLens": {
@@ -9456,11 +9460,11 @@ fn lsp_vendor_dir() {
"imports": {}
},
"unstable": false
- }]);
+ } });
// one for the workspace
- client.handle_configuration_request(request.clone());
+ client.handle_configuration_request(&settings);
// one for the specifier
- client.handle_configuration_request(request);
+ client.handle_configuration_request(&settings);
};
refresh_config(&mut client);
diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs
index 2af27e8d4..72d27f6d0 100644
--- a/test_util/src/lsp.rs
+++ b/test_util/src/lsp.rs
@@ -685,9 +685,9 @@ impl LspClient {
) {
self.initialize_with_config(
do_build,
- json!([{
+ json!({"deno":{
"enable": true
- }]),
+ }}),
)
}
@@ -709,18 +709,18 @@ impl LspClient {
self.write_notification("initialized", json!({}));
self.config = config;
if self.supports_workspace_configuration {
- self.handle_configuration_request(self.config.clone());
+ self.handle_configuration_request(&self.config.clone());
}
}
pub fn did_open(&mut self, params: Value) -> CollectedDiagnostics {
- self.did_open_with_config(params, self.config.clone())
+ self.did_open_with_config(params, &self.config.clone())
}
pub fn did_open_with_config(
&mut self,
params: Value,
- config: Value,
+ config: &Value,
) -> CollectedDiagnostics {
self.did_open_raw(params);
if self.supports_workspace_configuration {
@@ -733,9 +733,18 @@ impl LspClient {
self.write_notification("textDocument/didOpen", params);
}
- pub fn handle_configuration_request(&mut self, result: Value) {
- let (id, method, _) = self.read_request::<Value>();
+ pub fn handle_configuration_request(&mut self, settings: &Value) {
+ let (id, method, args) = self.read_request::<Value>();
assert_eq!(method, "workspace/configuration");
+ let params = args.as_ref().unwrap().as_object().unwrap();
+ let items = params.get("items").unwrap().as_array().unwrap();
+ let settings_object = settings.as_object().unwrap();
+ let mut result = vec![];
+ for item in items {
+ let item = item.as_object().unwrap();
+ let section = item.get("section").unwrap().as_str().unwrap();
+ result.push(settings_object.get(section).cloned().unwrap_or_default());
+ }
self.write_response(id, result);
}