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::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(

View file

@ -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()

View file

@ -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?;

View file

@ -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,
},
},
}
}

View file

@ -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)

View file

@ -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);

View file

@ -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);
}