1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

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.
This commit is contained in:
Nayeem Rahman 2023-09-21 06:46:39 +01:00 committed by GitHub
parent 0981aefbdc
commit a4ac6a3f5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 501 additions and 185 deletions

View file

@ -7,7 +7,6 @@ use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::Value;
use deno_core::unsync::spawn; use deno_core::unsync::spawn;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::ConfigurationItem; 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 crate::lsp::repl::get_repl_workspace_settings;
use super::config::SpecifierSettings; use super::config::SpecifierSettings;
use super::config::WorkspaceSettings;
use super::config::SETTINGS_SECTION; use super::config::SETTINGS_SECTION;
use super::lsp_custom; use super::lsp_custom;
use super::testing::lsp_custom as testing_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 self.0.workspace_configuration().await
} }
@ -186,7 +188,9 @@ trait ClientTrait: Send + Sync {
&self, &self,
uris: Vec<lsp::Url>, uris: Vec<lsp::Url>,
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError>; ) -> 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 show_message(&self, message_type: lsp::MessageType, text: String);
async fn register_capability( async fn register_capability(
&self, &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 let config_response = self
.0 .0
.configuration(vec![ConfigurationItem { .configuration(vec![
scope_uri: None, ConfigurationItem {
section: Some(SETTINGS_SECTION.to_string()), 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; .await;
match config_response { match config_response {
Ok(value_vec) => match value_vec.get(0).cloned() { Ok(configs) => {
Some(value) => Ok(value), let mut configs = configs.into_iter();
None => bail!("Missing response workspace configuration."), 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) => { Err(err) => {
bail!("Error getting workspace configuration: {}", err) bail!("Error getting workspace configuration: {}", err)
} }
@ -367,8 +388,10 @@ impl ClientTrait for ReplClient {
Ok(settings) Ok(settings)
} }
async fn workspace_configuration(&self) -> Result<Value, AnyError> { async fn workspace_configuration(
Ok(serde_json::to_value(get_repl_workspace_settings()).unwrap()) &self,
) -> Result<WorkspaceSettings, AnyError> {
Ok(get_repl_workspace_settings())
} }
async fn show_message( async fn show_message(

View file

@ -5,8 +5,9 @@ use crate::args::ConfigFile;
use crate::lsp::logging::lsp_warn; use crate::lsp::logging::lsp_warn;
use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::path::specifier_to_file_path; 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::parking_lot::Mutex;
use deno_core::serde::de::DeserializeOwned;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
use deno_core::serde_json; 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)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CompletionSettings { pub struct CompletionSettings {
@ -97,8 +105,6 @@ pub struct CompletionSettings {
pub paths: bool, pub paths: bool,
#[serde(default = "is_true")] #[serde(default = "is_true")]
pub auto_imports: bool, pub auto_imports: bool,
#[serde(default)]
pub imports: ImportCompletionSettings,
} }
impl Default for CompletionSettings { impl Default for CompletionSettings {
@ -108,7 +114,6 @@ impl Default for CompletionSettings {
names: true, names: true,
paths: true, paths: true,
auto_imports: 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())) 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. /// Deno language server specific settings that are applied to a workspace.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -319,9 +333,6 @@ pub struct WorkspaceSettings {
#[serde(default)] #[serde(default)]
pub code_lens: CodeLensSettings, pub code_lens: CodeLensSettings,
#[serde(default)]
pub inlay_hints: InlayHintsSettings,
/// A flag that indicates if internal debug logging should be made available. /// A flag that indicates if internal debug logging should be made available.
#[serde(default)] #[serde(default)]
pub internal_debug: bool, pub internal_debug: bool,
@ -334,10 +345,8 @@ pub struct WorkspaceSettings {
#[serde(default = "default_document_preload_limit")] #[serde(default = "default_document_preload_limit")]
pub document_preload_limit: usize, pub document_preload_limit: usize,
/// A flag that indicates if Dene should validate code against the unstable
/// APIs for the workspace.
#[serde(default)] #[serde(default)]
pub suggest: CompletionSettings, pub suggest: DenoCompletionSettings,
/// Testing settings for the workspace. /// Testing settings for the workspace.
#[serde(default)] #[serde(default)]
@ -355,6 +364,12 @@ pub struct WorkspaceSettings {
#[serde(default)] #[serde(default)]
pub unstable: bool, pub unstable: bool,
#[serde(default)]
pub javascript: LanguageWorkspaceSettings,
#[serde(default)]
pub typescript: LanguageWorkspaceSettings,
} }
impl Default for WorkspaceSettings { impl Default for WorkspaceSettings {
@ -368,7 +383,6 @@ impl Default for WorkspaceSettings {
config: None, config: None,
import_map: None, import_map: None,
code_lens: Default::default(), code_lens: Default::default(),
inlay_hints: Default::default(),
internal_debug: false, internal_debug: false,
lint: true, lint: true,
document_preload_limit: default_document_preload_limit(), document_preload_limit: default_document_preload_limit(),
@ -377,28 +391,220 @@ impl Default for WorkspaceSettings {
tls_certificate: None, tls_certificate: None,
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
unstable: false, unstable: false,
javascript: Default::default(),
typescript: Default::default(),
} }
} }
} }
impl WorkspaceSettings { 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 /// Determine if any code lenses are enabled at all. This allows short
/// circuiting when there are no code lenses enabled. /// circuiting when there are no code lenses enabled.
pub fn enabled_code_lens(&self) -> bool { pub fn enabled_code_lens(&self) -> bool {
self.code_lens.implementations || self.code_lens.references 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 /// Determine if any inlay hints are enabled. This allows short circuiting
/// when there are no inlay hints enabled. /// 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!( !matches!(
self.inlay_hints.parameter_names.enabled, settings.inlay_hints.parameter_names.enabled,
InlayHintsParamNamesEnabled::None InlayHintsParamNamesEnabled::None
) || self.inlay_hints.parameter_types.enabled ) || settings.inlay_hints.parameter_types.enabled
|| self.inlay_hints.variable_types.enabled || settings.inlay_hints.variable_types.enabled
|| self.inlay_hints.property_declaration_types.enabled || settings.inlay_hints.property_declaration_types.enabled
|| self.inlay_hints.function_like_return_types.enabled || settings.inlay_hints.function_like_return_types.enabled
|| self.inlay_hints.enum_member_values.enabled || settings.inlay_hints.enum_member_values.enabled
} }
} }
@ -593,16 +799,12 @@ impl Config {
/// Set the workspace settings directly, which occurs during initialization /// Set the workspace settings directly, which occurs during initialization
/// and when the client does not support workspace configuration requests /// and when the client does not support workspace configuration requests
pub fn set_workspace_settings( pub fn set_workspace_settings(&mut self, settings: WorkspaceSettings) {
&mut self, self.settings.workspace = settings;
value: Value,
) -> Result<(), AnyError> {
self.settings.workspace = serde_json::from_value(value)?;
// See https://github.com/denoland/vscode_deno/issues/908. // See https://github.com/denoland/vscode_deno/issues/908.
if self.settings.workspace.enable_paths == Some(vec![]) { if self.settings.workspace.enable_paths == Some(vec![]) {
self.settings.workspace.enable_paths = None; self.settings.workspace.enable_paths = None;
} }
Ok(())
} }
pub fn snapshot(&self) -> Arc<ConfigSnapshot> { pub fn snapshot(&self) -> Arc<ConfigSnapshot> {
@ -912,6 +1114,7 @@ fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option<Lockfile> {
mod tests { mod tests {
use super::*; use super::*;
use deno_core::resolve_url; use deno_core::resolve_url;
use deno_core::serde_json;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -921,11 +1124,12 @@ mod tests {
let mut config = Config::new_with_root(root_uri); let mut config = Config::new_with_root(root_uri);
let specifier = resolve_url("file:///a.ts").unwrap(); let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier)); assert!(!config.specifier_enabled(&specifier));
config config.set_workspace_settings(
.set_workspace_settings(json!({ serde_json::from_value(json!({
"enable": true "enable": true
})) }))
.expect("could not update"); .unwrap(),
);
assert!(config.specifier_enabled(&specifier)); assert!(config.specifier_enabled(&specifier));
} }
@ -935,11 +1139,12 @@ mod tests {
let mut config = Config::new_with_root(root_uri); let mut config = Config::new_with_root(root_uri);
let specifier = resolve_url("file:///a.ts").unwrap(); let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier)); assert!(!config.specifier_enabled(&specifier));
config config.set_workspace_settings(
.set_workspace_settings(json!({ serde_json::from_value(json!({
"enable": true "enable": true
})) }))
.expect("could not update"); .unwrap(),
);
let config_snapshot = config.snapshot(); let config_snapshot = config.snapshot();
assert!(config_snapshot.specifier_enabled(&specifier)); assert!(config_snapshot.specifier_enabled(&specifier));
} }
@ -954,7 +1159,7 @@ mod tests {
assert!(!config.specifier_enabled(&specifier_b)); assert!(!config.specifier_enabled(&specifier_b));
let workspace_settings = let workspace_settings =
serde_json::from_str(r#"{ "enablePaths": ["worker"] }"#).unwrap(); 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_a));
assert!(!config.specifier_enabled(&specifier_b)); assert!(!config.specifier_enabled(&specifier_b));
let config_snapshot = config.snapshot(); let config_snapshot = config.snapshot();
@ -979,9 +1184,7 @@ mod tests {
#[test] #[test]
fn test_set_workspace_settings_defaults() { fn test_set_workspace_settings_defaults() {
let mut config = Config::new(); let mut config = Config::new();
config config.set_workspace_settings(serde_json::from_value(json!({})).unwrap());
.set_workspace_settings(json!({}))
.expect("could not update");
assert_eq!( assert_eq!(
config.workspace_settings().clone(), config.workspace_settings().clone(),
WorkspaceSettings { WorkspaceSettings {
@ -998,34 +1201,10 @@ mod tests {
references_all_functions: false, references_all_functions: false,
test: true, 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, internal_debug: false,
lint: true, lint: true,
document_preload_limit: 1_000, document_preload_limit: 1_000,
suggest: CompletionSettings { suggest: DenoCompletionSettings {
complete_function_calls: false,
names: true,
paths: true,
auto_imports: true,
imports: ImportCompletionSettings { imports: ImportCompletionSettings {
auto_discover: true, auto_discover: true,
hosts: HashMap::new(), hosts: HashMap::new(),
@ -1037,6 +1216,62 @@ mod tests {
tls_certificate: None, tls_certificate: None,
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
unstable: false, 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] #[test]
fn test_empty_cache() { fn test_empty_cache() {
let mut config = Config::new(); let mut config = Config::new();
config config.set_workspace_settings(
.set_workspace_settings(json!({ "cache": "" })) serde_json::from_value(json!({ "cache": "" })).unwrap(),
.expect("could not update"); );
assert_eq!( assert_eq!(
config.workspace_settings().clone(), config.workspace_settings().clone(),
WorkspaceSettings::default() WorkspaceSettings::default()
@ -1056,9 +1291,9 @@ mod tests {
#[test] #[test]
fn test_empty_import_map() { fn test_empty_import_map() {
let mut config = Config::new(); let mut config = Config::new();
config config.set_workspace_settings(
.set_workspace_settings(json!({ "import_map": "" })) serde_json::from_value(json!({ "import_map": "" })).unwrap(),
.expect("could not update"); );
assert_eq!( assert_eq!(
config.workspace_settings().clone(), config.workspace_settings().clone(),
WorkspaceSettings::default() WorkspaceSettings::default()
@ -1068,9 +1303,9 @@ mod tests {
#[test] #[test]
fn test_empty_tls_certificate() { fn test_empty_tls_certificate() {
let mut config = Config::new(); let mut config = Config::new();
config config.set_workspace_settings(
.set_workspace_settings(json!({ "tls_certificate": "" })) serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
.expect("could not update"); );
assert_eq!( assert_eq!(
config.workspace_settings().clone(), config.workspace_settings().clone(),
WorkspaceSettings::default() WorkspaceSettings::default()
@ -1080,9 +1315,9 @@ mod tests {
#[test] #[test]
fn test_empty_config() { fn test_empty_config() {
let mut config = Config::new(); let mut config = Config::new();
config config.set_workspace_settings(
.set_workspace_settings(json!({ "config": "" })) serde_json::from_value(json!({ "config": "" })).unwrap(),
.expect("could not update"); );
assert_eq!( assert_eq!(
config.workspace_settings().clone(), config.workspace_settings().clone(),
WorkspaceSettings::default() WorkspaceSettings::default()

View file

@ -45,6 +45,7 @@ use super::code_lens;
use super::completions; use super::completions;
use super::config::Config; use super::config::Config;
use super::config::ConfigSnapshot; use super::config::ConfigSnapshot;
use super::config::WorkspaceSettings;
use super::config::SETTINGS_SECTION; use super::config::SETTINGS_SECTION;
use super::diagnostics; use super::diagnostics;
use super::diagnostics::DiagnosticServerUpdateMessage; use super::diagnostics::DiagnosticServerUpdateMessage;
@ -1265,11 +1266,10 @@ impl Inner {
} }
{ {
if let Some(value) = params.initialization_options { if let Some(options) = params.initialization_options {
self.config.set_workspace_settings(value).map_err(|err| { self.config.set_workspace_settings(
error!("Cannot set workspace settings: {}", err); WorkspaceSettings::from_initialization_options(options),
LspError::internal_error() );
})?;
} }
if let Some(folders) = params.workspace_folders { if let Some(folders) = params.workspace_folders {
self.config.workspace_folders = folders self.config.workspace_folders = folders
@ -1472,24 +1472,26 @@ impl Inner {
async fn did_change_configuration( async fn did_change_configuration(
&mut self, &mut self,
client_workspace_config: Option<Value>, client_workspace_config: Option<WorkspaceSettings>,
params: DidChangeConfigurationParams, params: DidChangeConfigurationParams,
) { ) {
let maybe_config = let maybe_config =
if self.config.client_capabilities.workspace_configuration { if self.config.client_capabilities.workspace_configuration {
client_workspace_config client_workspace_config
} else { } else {
params params.settings.as_object().map(|settings| {
.settings let deno =
.as_object() serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
.and_then(|settings| settings.get(SETTINGS_SECTION)) let javascript =
.cloned() 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 Some(settings) = maybe_config {
if let Err(err) = self.config.set_workspace_settings(value) { self.config.set_workspace_settings(settings);
error!("failed to update settings: {}", err);
}
} }
self.update_debug_flag(); self.update_debug_flag();
@ -1929,7 +1931,10 @@ impl Inner {
(&self.fmt_options.options).into(), (&self.fmt_options.options).into(),
tsc::UserPreferences { tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()), 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; .await;
@ -1990,7 +1995,10 @@ impl Inner {
..line_index.offset_tsc(params.range.end)?, ..line_index.offset_tsc(params.range.end)?,
Some(tsc::UserPreferences { Some(tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()), 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, only,
) )
@ -2049,7 +2057,10 @@ impl Inner {
(&self.fmt_options.options).into(), (&self.fmt_options.options).into(),
tsc::UserPreferences { tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()), 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?; .await?;
@ -2090,7 +2101,7 @@ impl Inner {
.ts_server .ts_server
.get_edits_for_refactor( .get_edits_for_refactor(
self.snapshot(), self.snapshot(),
action_data.specifier, action_data.specifier.clone(),
(&self.fmt_options.options).into(), (&self.fmt_options.options).into(),
line_index.offset_tsc(action_data.range.start)? line_index.offset_tsc(action_data.range.start)?
..line_index.offset_tsc(action_data.range.end)?, ..line_index.offset_tsc(action_data.range.end)?,
@ -2098,7 +2109,10 @@ impl Inner {
action_data.action_name, action_data.action_name,
Some(tsc::UserPreferences { Some(tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()), 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?; .await?;
@ -2425,9 +2439,11 @@ impl Inner {
), ),
include_automatic_optional_chain_completions: Some(true), include_automatic_optional_chain_completions: Some(true),
include_completions_for_import_statements: Some(true), include_completions_for_import_statements: Some(true),
include_completions_for_module_exports: Some( include_completions_for_module_exports: self
self.config.workspace_settings().suggest.auto_imports, .config
), .workspace_settings()
.language_settings_for_specifier(&specifier)
.map(|s| s.suggest.auto_imports),
include_completions_with_object_literal_method_snippets: Some( include_completions_with_object_literal_method_snippets: Some(
use_snippets, use_snippets,
), ),
@ -2454,7 +2470,13 @@ impl Inner {
if let Some(completions) = maybe_completion_info { if let Some(completions) = maybe_completion_info {
let results = completions.as_completion_response( let results = completions.as_completion_response(
line_index, line_index,
&self.config.workspace_settings().suggest, &self
.config
.workspace_settings()
.language_settings_for_specifier(&specifier)
.cloned()
.unwrap_or_default()
.suggest,
&specifier, &specifier,
position, position,
self, self,
@ -3284,7 +3306,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
.workspace_configuration() .workspace_configuration()
.await; .await;
match config_response { match config_response {
Ok(value) => Some(value), Ok(settings) => Some(settings),
Err(err) => { Err(err) => {
error!("{}", err); error!("{}", err);
None None
@ -3601,7 +3623,7 @@ impl Inner {
let workspace_settings = self.config.workspace_settings(); let workspace_settings = self.config.workspace_settings();
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
|| !workspace_settings.enabled_inlay_hints() || !workspace_settings.enabled_inlay_hints(&specifier)
{ {
return Ok(None); return Ok(None);
} }
@ -3620,11 +3642,14 @@ impl Inner {
.ts_server .ts_server
.provide_inlay_hints( .provide_inlay_hints(
self.snapshot(), self.snapshot(),
specifier, specifier.clone(),
text_span, text_span,
tsc::UserPreferences { tsc::UserPreferences {
quote_preference: Some((&self.fmt_options.options).into()), quote_preference: Some((&self.fmt_options.options).into()),
..workspace_settings.into() ..tsc::UserPreferences::from_workspace_settings_for_specifier(
workspace_settings,
&specifier,
)
}, },
) )
.await?; .await?;

View file

@ -33,7 +33,9 @@ use tower_lsp::LanguageServer;
use super::client::Client; use super::client::Client;
use super::config::CompletionSettings; use super::config::CompletionSettings;
use super::config::DenoCompletionSettings;
use super::config::ImportCompletionSettings; use super::config::ImportCompletionSettings;
use super::config::LanguageWorkspaceSettings;
use super::config::TestingSettings; use super::config::TestingSettings;
use super::config::WorkspaceSettings; use super::config::WorkspaceSettings;
@ -292,23 +294,36 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
cache: None, cache: None,
import_map: None, import_map: None,
code_lens: Default::default(), code_lens: Default::default(),
inlay_hints: Default::default(),
internal_debug: false, internal_debug: false,
lint: false, lint: false,
document_preload_limit: 0, // don't pre-load any modules as it's expensive and not useful for the repl document_preload_limit: 0, // don't pre-load any modules as it's expensive and not useful for the repl
tls_certificate: None, tls_certificate: None,
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
unstable: false, unstable: false,
suggest: CompletionSettings { suggest: DenoCompletionSettings {
complete_function_calls: false,
names: false,
paths: false,
auto_imports: false,
imports: ImportCompletionSettings { imports: ImportCompletionSettings {
auto_discover: false, auto_discover: false,
hosts: HashMap::from([("https://deno.land".to_string(), true)]), hosts: HashMap::from([("https://deno.land".to_string(), true)]),
}, },
}, },
testing: TestingSettings { args: vec![] }, 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,
},
},
} }
} }

View file

@ -3665,36 +3665,35 @@ pub struct UserPreferences {
pub auto_import_file_exclude_patterns: Option<Vec<String>>, pub auto_import_file_exclude_patterns: Option<Vec<String>>,
} }
impl From<&config::WorkspaceSettings> for UserPreferences { impl UserPreferences {
fn from(workspace_settings: &config::WorkspaceSettings) -> Self { pub fn from_workspace_settings_for_specifier(
let inlay_hints = &workspace_settings.inlay_hints; settings: &config::WorkspaceSettings,
specifier: &ModuleSpecifier,
) -> Self {
let language_settings = settings.language_settings_for_specifier(specifier);
Self { Self {
include_inlay_parameter_name_hints: Some( include_inlay_parameter_name_hints: language_settings
(&inlay_hints.parameter_names.enabled).into(), .map(|s| (&s.inlay_hints.parameter_names.enabled).into()),
), include_inlay_parameter_name_hints_when_argument_matches_name:
include_inlay_parameter_name_hints_when_argument_matches_name: Some( language_settings.map(|s| {
!inlay_hints !s.inlay_hints
.parameter_names .parameter_names
.suppress_when_argument_matches_name, .suppress_when_argument_matches_name
), }),
include_inlay_function_parameter_type_hints: Some( include_inlay_function_parameter_type_hints: language_settings
inlay_hints.parameter_types.enabled, .map(|s| s.inlay_hints.parameter_types.enabled),
), include_inlay_variable_type_hints: language_settings
include_inlay_variable_type_hints: Some( .map(|s| s.inlay_hints.variable_types.enabled),
inlay_hints.variable_types.enabled, include_inlay_variable_type_hints_when_type_matches_name:
), language_settings.map(|s| {
include_inlay_variable_type_hints_when_type_matches_name: Some( !s.inlay_hints.variable_types.suppress_when_type_matches_name
!inlay_hints.variable_types.suppress_when_type_matches_name, }),
), include_inlay_property_declaration_type_hints: language_settings
include_inlay_property_declaration_type_hints: Some( .map(|s| s.inlay_hints.property_declaration_types.enabled),
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_function_like_return_type_hints: Some( include_inlay_enum_member_value_hints: language_settings
inlay_hints.function_like_return_types.enabled, .map(|s| s.inlay_hints.enum_member_values.enabled),
),
include_inlay_enum_member_value_hints: Some(
inlay_hints.enum_member_values.enabled,
),
..Default::default() ..Default::default()
} }
} }
@ -5153,14 +5152,20 @@ mod tests {
fn include_suppress_inlay_hit_settings() { fn include_suppress_inlay_hit_settings() {
let mut settings = WorkspaceSettings::default(); let mut settings = WorkspaceSettings::default();
settings settings
.typescript
.inlay_hints .inlay_hints
.parameter_names .parameter_names
.suppress_when_argument_matches_name = true; .suppress_when_argument_matches_name = true;
settings settings
.typescript
.inlay_hints .inlay_hints
.variable_types .variable_types
.suppress_when_type_matches_name = true; .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!( assert_eq!(
user_preferences.include_inlay_variable_type_hints_when_type_matches_name, user_preferences.include_inlay_variable_type_hints_when_type_matches_name,
Some(false) Some(false)

View file

@ -776,12 +776,12 @@ fn lsp_import_attributes() {
"text": "{\"a\":1}" "text": "{\"a\":1}"
} }
}), }),
json!([{ &json!({ "deno": {
"enable": true, "enable": true,
"codeLens": { "codeLens": {
"test": true "test": true
} }
}]), } }),
); );
let diagnostics = client.did_open(json!({ let diagnostics = client.did_open(json!({
@ -1131,7 +1131,7 @@ fn lsp_hover_disabled() {
"text": "console.log(Date.now());\n" "text": "console.log(Date.now());\n"
} }
}), }),
json!([{ "enable": false }]), &json!({ "deno": { "enable": false } }),
); );
let res = client.write_request( let res = client.write_request(
@ -1329,11 +1329,11 @@ fn lsp_workspace_disable_enable_paths() {
}]) }])
.set_deno_enable(false); .set_deno_enable(false);
}, },
json!([{ json!({ "deno": {
"enable": false, "enable": false,
"disablePaths": ["./worker/node.ts"], "disablePaths": ["./worker/node.ts"],
"enablePaths": ["./worker"], "enablePaths": ["./worker"],
}]), } }),
); );
client.did_open(json!({ client.did_open(json!({
@ -3552,12 +3552,12 @@ fn lsp_code_lens_test_disabled() {
} }
}), }),
// disable test code lens // disable test code lens
json!([{ &json!({ "deno": {
"enable": true, "enable": true,
"codeLens": { "codeLens": {
"test": false "test": false
} }
}]), } }),
); );
let res = client.write_request( let res = client.write_request(
"textDocument/codeLens", "textDocument/codeLens",
@ -5160,7 +5160,7 @@ fn lsp_code_actions_deadlock() {
"text": large_file_text, "text": large_file_text,
} }
})); }));
client.handle_configuration_request(json!([{ "enable": true }])); client.handle_configuration_request(&json!({ "deno": { "enable": true } }));
client.write_request( client.write_request(
"textDocument/semanticTokens/full", "textDocument/semanticTokens/full",
json!({ json!({
@ -5796,9 +5796,9 @@ fn lsp_semantic_tokens_for_disabled_module() {
|builder| { |builder| {
builder.set_deno_enable(false); builder.set_deno_enable(false);
}, },
json!({ json!({ "deno": {
"enable": false "enable": false
}), } }),
); );
client.did_open(json!({ client.did_open(json!({
"textDocument": { "textDocument": {
@ -8096,7 +8096,7 @@ fn lsp_configuration_did_change() {
"settings": {} "settings": {}
}), }),
); );
let request = json!([{ let settings = json!({ "deno": {
"enable": true, "enable": true,
"codeLens": { "codeLens": {
"implementations": true, "implementations": true,
@ -8116,11 +8116,11 @@ fn lsp_configuration_did_change() {
} }
}, },
"unstable": false "unstable": false
}]); } });
// one for the workspace // one for the workspace
client.handle_configuration_request(request.clone()); client.handle_configuration_request(&settings);
// one for the specifier // one for the specifier
client.handle_configuration_request(request); client.handle_configuration_request(&settings);
let list = client.get_completion_list( let list = client.get_completion_list(
"file:///a/file.ts", "file:///a/file.ts",
@ -8192,16 +8192,20 @@ fn lsp_completions_complete_function_calls() {
"settings": {} "settings": {}
}), }),
); );
let request = json!([{ let settings = json!({
"enable": true, "deno": {
"suggest": { "enable": true,
"completeFunctionCalls": true,
}, },
}]); "typescript": {
"suggest": {
"completeFunctionCalls": true,
},
},
});
// one for the workspace // one for the workspace
client.handle_configuration_request(request.clone()); client.handle_configuration_request(&settings);
// one for the specifier // one for the specifier
client.handle_configuration_request(request); client.handle_configuration_request(&settings);
let list = client.get_completion_list( let list = client.get_completion_list(
"file:///a/file.ts", "file:///a/file.ts",
@ -9304,7 +9308,7 @@ fn lsp_node_modules_dir() {
}), }),
); );
let request = json!([{ let settings = json!({ "deno": {
"enable": true, "enable": true,
"config": "./deno.json", "config": "./deno.json",
"codeLens": { "codeLens": {
@ -9321,11 +9325,11 @@ fn lsp_node_modules_dir() {
"imports": {} "imports": {}
}, },
"unstable": false "unstable": false
}]); } });
// one for the workspace // one for the workspace
client.handle_configuration_request(request.clone()); client.handle_configuration_request(&settings);
// one for the specifier // one for the specifier
client.handle_configuration_request(request); client.handle_configuration_request(&settings);
}; };
refresh_config(&mut client); refresh_config(&mut client);
@ -9439,7 +9443,7 @@ fn lsp_vendor_dir() {
}), }),
); );
let request = json!([{ let settings = json!({ "deno": {
"enable": true, "enable": true,
"config": "./deno.json", "config": "./deno.json",
"codeLens": { "codeLens": {
@ -9456,11 +9460,11 @@ fn lsp_vendor_dir() {
"imports": {} "imports": {}
}, },
"unstable": false "unstable": false
}]); } });
// one for the workspace // one for the workspace
client.handle_configuration_request(request.clone()); client.handle_configuration_request(&settings);
// one for the specifier // one for the specifier
client.handle_configuration_request(request); client.handle_configuration_request(&settings);
}; };
refresh_config(&mut client); refresh_config(&mut client);

View file

@ -685,9 +685,9 @@ impl LspClient {
) { ) {
self.initialize_with_config( self.initialize_with_config(
do_build, do_build,
json!([{ json!({"deno":{
"enable": true "enable": true
}]), }}),
) )
} }
@ -709,18 +709,18 @@ impl LspClient {
self.write_notification("initialized", json!({})); self.write_notification("initialized", json!({}));
self.config = config; self.config = config;
if self.supports_workspace_configuration { 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 { 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( pub fn did_open_with_config(
&mut self, &mut self,
params: Value, params: Value,
config: Value, config: &Value,
) -> CollectedDiagnostics { ) -> CollectedDiagnostics {
self.did_open_raw(params); self.did_open_raw(params);
if self.supports_workspace_configuration { if self.supports_workspace_configuration {
@ -733,9 +733,18 @@ impl LspClient {
self.write_notification("textDocument/didOpen", params); self.write_notification("textDocument/didOpen", params);
} }
pub fn handle_configuration_request(&mut self, result: Value) { pub fn handle_configuration_request(&mut self, settings: &Value) {
let (id, method, _) = self.read_request::<Value>(); let (id, method, args) = self.read_request::<Value>();
assert_eq!(method, "workspace/configuration"); 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); self.write_response(id, result);
} }