From 644a1f5a81a03cf9138d6b5a4bd751bb0d7ad21e Mon Sep 17 00:00:00 2001 From: farwyler <1705805+farwyler@users.noreply.github.com> Date: Wed, 1 Jun 2022 08:05:39 +0200 Subject: [PATCH 1/4] allows passing extra formatting options to LSPs - adds optional field 'format' to [[language]] sections in 'languages.toml' - passes specified options the LSPs via FormattingOptions --- book/src/languages.md | 12 ++++++++++++ helix-core/src/syntax.rs | 3 +++ helix-view/src/document.rs | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/book/src/languages.md b/book/src/languages.md index 1fa2478795ee..b446e4de0889 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -14,6 +14,18 @@ name = "rust" auto-format = false ``` +## LSP formatting options + +Use `format` field to pass extra formatting options to [Document Formatting Requests](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-16.md#document-formatting-request--leftwards_arrow_with_hook). + +```toml +[[language]] +name = "typescript" +auto-format = true +# pass format options according to https://github.com/typescript-language-server/typescript-language-server#workspacedidchangeconfiguration omitting the "[language].format." prefix. +format = { "semicolons" = "insert", "insertSpaceBeforeFunctionParenthesis" = true } +``` + ## Tree-sitter grammars Tree-sitter grammars can also be configured in `languages.toml`: diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 1cfa04e7c194..4c0149ea11df 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -78,6 +78,9 @@ pub struct LanguageConfiguration { #[serde(default)] pub auto_format: bool, + #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")] + pub format: Option, + #[serde(default)] pub diagnostic_severity: Severity, diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 9c3853c828c5..7f86d6cd80ac 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -410,11 +410,33 @@ impl Document { let language_server = self.language_server()?; let text = self.text.clone(); let offset_encoding = language_server.offset_encoding(); + + let mut properties = HashMap::new(); + if let Some(fmt) = self + .language_config() + .and_then(|cfg| cfg.format.as_ref().and_then(|c| c.as_object())) + { + for (key, value) in fmt { + let prop = if let Some(s) = value.as_str() { + lsp::FormattingProperty::String(s.to_owned()) + } else if let Some(b) = value.as_bool() { + lsp::FormattingProperty::Bool(b) + } else if let Some(n) = value.as_i64() { + lsp::FormattingProperty::Number(n as i32) + } else { + log::warn!("invalid formatting property type {:?} for {}", value, key); + continue; + }; + properties.insert(key.to_owned(), prop); + } + } + let request = language_server.text_document_formatting( self.identifier(), lsp::FormattingOptions { tab_size: self.tab_width() as u32, insert_spaces: matches!(self.indent_style, IndentStyle::Spaces(_)), + properties, ..Default::default() }, None, From 563fccf0a6b248e31ea13ac83b4e0ecb2f054381 Mon Sep 17 00:00:00 2001 From: farwyler <1705805+farwyler@users.noreply.github.com> Date: Wed, 1 Jun 2022 10:34:30 +0200 Subject: [PATCH 2/4] cleaner conversion of formatting properties --- helix-view/src/document.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 7f86d6cd80ac..1c3952c56000 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -416,18 +416,10 @@ impl Document { .language_config() .and_then(|cfg| cfg.format.as_ref().and_then(|c| c.as_object())) { - for (key, value) in fmt { - let prop = if let Some(s) = value.as_str() { - lsp::FormattingProperty::String(s.to_owned()) - } else if let Some(b) = value.as_bool() { - lsp::FormattingProperty::Bool(b) - } else if let Some(n) = value.as_i64() { - lsp::FormattingProperty::Number(n as i32) - } else { - log::warn!("invalid formatting property type {:?} for {}", value, key); - continue; - }; - properties.insert(key.to_owned(), prop); + for (key, value) in fmt.iter() { + if let Ok(prop) = serde_json::from_value(value.clone()) { + properties.insert(key.to_owned(), prop); + } } } From 6c29b0e79cc98ec73bce67154826127253546533 Mon Sep 17 00:00:00 2001 From: farwyler <1705805+farwyler@users.noreply.github.com> Date: Thu, 2 Jun 2022 08:11:24 +0200 Subject: [PATCH 3/4] move formatting options inside lsp::Client --- book/src/languages.md | 2 +- helix-core/src/syntax.rs | 2 -- helix-lsp/src/client.rs | 22 +++++++++++++++++++++- helix-view/src/document.rs | 13 ------------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/book/src/languages.md b/book/src/languages.md index b446e4de0889..8c27785e2197 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -23,7 +23,7 @@ Use `format` field to pass extra formatting options to [Document Formatting Requ name = "typescript" auto-format = true # pass format options according to https://github.com/typescript-language-server/typescript-language-server#workspacedidchangeconfiguration omitting the "[language].format." prefix. -format = { "semicolons" = "insert", "insertSpaceBeforeFunctionParenthesis" = true } +config = { format = { "semicolons" = "insert", "insertSpaceBeforeFunctionParenthesis" = true } } ``` ## Tree-sitter grammars diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 4c0149ea11df..ca497b648229 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -78,8 +78,6 @@ pub struct LanguageConfiguration { #[serde(default)] pub auto_format: bool, - #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")] - pub format: Option, #[serde(default)] pub diagnostic_severity: Severity, diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 08201b3f4268..55143de5991e 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -8,6 +8,7 @@ use helix_core::{find_root, ChangeSet, Rope}; use jsonrpc_core as jsonrpc; use lsp_types as lsp; use serde_json::Value; +use std::collections::HashMap; use std::future::Future; use std::process::Stdio; use std::sync::{ @@ -693,9 +694,28 @@ impl Client { }; // TODO: return err::unavailable so we can fall back to tree sitter formatting + // merge FormattingOptions with values from lsp config + let mut merged_options = options.clone(); + if let Some(format) = self + .config + .as_ref() + .and_then(|cfg| cfg.get("format")) + .and_then(|fmt| fmt.as_object()) + { + for (key, value) in format { + // upstream properties take precedence + if merged_options.properties.get(key).is_some() { + continue; + } + if let Ok(prop) = serde_json::from_value(value.clone()) { + merged_options.properties.insert(key.to_owned(), prop); + } + } + } + let params = lsp::DocumentFormattingParams { text_document, - options, + options: merged_options, work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, }; diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 1c3952c56000..2c4b5de96b95 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -411,24 +411,11 @@ impl Document { let text = self.text.clone(); let offset_encoding = language_server.offset_encoding(); - let mut properties = HashMap::new(); - if let Some(fmt) = self - .language_config() - .and_then(|cfg| cfg.format.as_ref().and_then(|c| c.as_object())) - { - for (key, value) in fmt.iter() { - if let Ok(prop) = serde_json::from_value(value.clone()) { - properties.insert(key.to_owned(), prop); - } - } - } - let request = language_server.text_document_formatting( self.identifier(), lsp::FormattingOptions { tab_size: self.tab_width() as u32, insert_spaces: matches!(self.indent_style, IndentStyle::Spaces(_)), - properties, ..Default::default() }, None, From 7c7779b7d9ba6fc424281a4f8af5d951e2e75f0a Mon Sep 17 00:00:00 2001 From: farwyler <1705805+farwyler@users.noreply.github.com> Date: Thu, 2 Jun 2022 12:41:12 +0200 Subject: [PATCH 4/4] cleans up formatting properties merge --- helix-lsp/src/client.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 55143de5991e..7f556ca6d8ef 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -7,6 +7,7 @@ use anyhow::anyhow; use helix_core::{find_root, ChangeSet, Rope}; use jsonrpc_core as jsonrpc; use lsp_types as lsp; +use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; use std::future::Future; @@ -694,28 +695,27 @@ impl Client { }; // TODO: return err::unavailable so we can fall back to tree sitter formatting - // merge FormattingOptions with values from lsp config - let mut merged_options = options.clone(); - if let Some(format) = self + // merge FormattingOptions with 'config.format' + let config_format = self .config .as_ref() .and_then(|cfg| cfg.get("format")) - .and_then(|fmt| fmt.as_object()) - { - for (key, value) in format { - // upstream properties take precedence - if merged_options.properties.get(key).is_some() { - continue; - } - if let Ok(prop) = serde_json::from_value(value.clone()) { - merged_options.properties.insert(key.to_owned(), prop); - } + .and_then(|fmt| HashMap::::deserialize(fmt).ok()); + + let options = if let Some(mut properties) = config_format { + // passed in options take precedence over 'config.format' + properties.extend(options.properties); + lsp::FormattingOptions { + properties, + ..options } - } + } else { + options + }; let params = lsp::DocumentFormattingParams { text_document, - options: merged_options, + options, work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, };