Repository: autozimu/LanguageClient-neovim (language: rust)

Issue:
MarkupKind is not properly set
This language client didn't set its `MarkupKind` in the `TextDocumentClientCapabilities`, therefore language servers may just pick one from their own preferences. 

1. AFAIK, most vim client doesn't have the real GUI ability to render markdown, sometimes it can be worse than `plaintext`, since `plaintext` are usually optimized for TUI.

2. Noticed that even we can set `2.21 g:LanguageClient_hoverPreview` to `Never`, language servers are likely to response more contents when `markdown` is assumed thus won't fit in one line.

Since `plaintext` also has its own drawbacks, we could introduce a `g` variable for plugin users to choose, ideally per language server.

Or, we could set the `MarkupKind` to `plaintext` when `hoverPreview`  is `Never`. But I think one might want some combinations such as `FloatingWindow + Plaintext` for simplicity.

This is softly connected to #835 which downgraded the preview window experience. Hopefully neovim's floating window will soon landed.


Discussion / hints:

Fix patch:
diff --git a/doc/LanguageClient.txt b/doc/LanguageClient.txt
index 866b26b18..f1b882d6c 100644
--- a/doc/LanguageClient.txt
+++ b/doc/LanguageClient.txt
@@ -400,7 +400,7 @@ the root of a project is detected using g:LanguageClient_rootMarkers.
 Default: 1 to display the messages
 Valid options: 1 | 0
 
-2.30 g:LanguageClient_semanticHighlightMaps   *g:LanguageClient_semanticHighlightMaps*
+2.32 g:LanguageClient_semanticHighlightMaps   *g:LanguageClient_semanticHighlightMaps*
 
 String to list/map map. Defines the mapping of semantic highlighting "scopes" to
 highlight groups. This depends on the LSP server supporting the proposed
@@ -501,13 +501,45 @@ Example configuration for eclipse.jdt.ls:
      highlight! JavaMemberVariable ctermfg=White cterm=italic guifg=White gui=italic
 
 
-2.31 g:LanguageClient_applyCompletionAdditionalTextEdits *g:LanguageClient__applyCompletionAdditionalTextEdits*
+2.33 g:LanguageClient_applyCompletionAdditionalTextEdits *g:LanguageClient__applyCompletionAdditionalTextEdits*
 
 Indicates whether completionItem additional text edits should be applied.
 
 Default: 1
 Valid options: 1 | 0
 
+2.34 g:LanguageClient_preferredMarkupKind *g:LanguageClient_preferredMarkupKind*
+
+Sets the preferred markup kind. This value is sent to the server and is
+normally used to decide whether to send plaintext or markdown in things like
+hover or completion items docunmentation.
+
+This should be set to an array of values with the preferred markup kinds in
+order of preferrence. Leaving this config unset, will send `null` to the
+server, effectively letting it decide which markup kind to use.
+
+Example setting 1. Set the preferred markup kind to `plaintext`
+  ```
+  let g:LanguageClient_preferredMarkupKind: ['plaintext']
+  ```
+
+Example setting 2. Set the preferred markup kind to `plaintext` and `markdown`
+as a fallback.
+
+  ```
+  let g:LanguageClient_preferredMarkupKind: ['plaintext', 'markdown']
+  ```
+
+Example setting 3. Set the preferred markup kind to `markdown`
+
+  ```
+  let g:LanguageClient_preferredMarkupKind: ['markdown']
+
+This setting may have no effect of the server decides not to honour it.
+
+Default: v:null
+Valid options: Array<String>
+
 ==============================================================================
 3. Commands                                           *LanguageClientCommands*
 
diff --git a/src/language_server_protocol.rs b/src/language_server_protocol.rs
index 1009aa225..12cd90f56 100644
--- a/src/language_server_protocol.rs
+++ b/src/language_server_protocol.rs
@@ -125,6 +125,7 @@ impl LanguageClient {
             semanticHighlightMaps,
             semanticScopeSeparator,
             applyCompletionAdditionalTextEdits,
+            preferred_markup_kind,
         ): (
             Option<u64>,
             String,
@@ -135,6 +136,7 @@ impl LanguageClient {
             HashMap<String, HashMap<String, String>>,
             String,
             u8,
+            Option<Vec<String>>,
         ) = self.vim()?.eval(
             [
                 "get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)",
@@ -146,6 +148,7 @@ impl LanguageClient {
                 "s:GetVar('LanguageClient_semanticHighlightMaps', {})",
                 "s:GetVar('LanguageClient_semanticScopeSeparator', ':')",
                 "get(g:, 'LanguageClient_applyCompletionAdditionalTextEdits', 1)",
+                "get(g:, 'LanguageClient_preferredMarkupKind', v:null)",
             ]
             .as_ref(),
         )?;
@@ -223,6 +226,18 @@ impl LanguageClient {
         let semanticHlUpdateLanguageIds: Vec<String> =
             semanticHighlightMaps.keys().cloned().collect();
 
+        let preferred_markup_kind: Option<Vec<MarkupKind>> = if preferred_markup_kind.is_some() {
+            serde_json::from_value(Value::Array(
+                preferred_markup_kind
+                    .unwrap()
+                    .into_iter()
+                    .map(|x| Value::String(x))
+                    .collect(),
+            ))?
+        } else {
+            None
+        };
+
         self.update(|state| {
             state.autoStart = autoStart;
             state.semanticHighlightMaps = semanticHighlightMaps;
@@ -258,6 +273,7 @@ impl LanguageClient {
             state.loggingLevel = loggingLevel;
             state.serverStderr = serverStderr;
             state.is_nvim = is_nvim;
+            state.preferred_markup_kind = preferred_markup_kind;
             Ok(())
         })?;
 
@@ -1119,6 +1135,7 @@ impl LanguageClient {
         };
 
         let trace = self.get(|state| state.trace)?;
+        let preferred_markup_kind = self.get(|state| state.preferred_markup_kind.clone())?;
 
         let result: Value = self.get_client(&Some(languageId.clone()))?.call(
             lsp::request::Initialize::METHOD,
@@ -1138,13 +1155,14 @@ impl LanguageClient {
                         completion: Some(CompletionCapability {
                             completion_item: Some(CompletionItemCapability {
                                 snippet_support: Some(has_snippet_support),
+                                documentation_format: preferred_markup_kind.clone(),
                                 ..CompletionItemCapability::default()
                             }),
                             ..CompletionCapability::default()
                         }),
                         signature_help: Some(SignatureHelpCapability {
                             signature_information: Some(SignatureInformationSettings {
-                                documentation_format: None,
+                                documentation_format: preferred_markup_kind.clone(),
                                 parameter_information: Some(ParameterInformationSettings {
                                     label_offset_support: Some(true),
                                 }),
@@ -1179,6 +1197,10 @@ impl LanguageClient {
                                 semantic_highlighting: true,
                             },
                         ),
+                        hover: Some(HoverCapability {
+                            content_format: preferred_markup_kind,
+                            ..HoverCapability::default()
+                        }),
                         ..TextDocumentClientCapabilities::default()
                     }),
                     workspace: Some(WorkspaceClientCapabilities {
diff --git a/src/types.rs b/src/types.rs
index 581792fc8..1e0402212 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -186,6 +186,7 @@ pub struct State {
     pub serverStderr: Option<String>,
     #[serde(skip_serializing)]
     pub logger: log4rs::Handle,
+    pub preferred_markup_kind: Option<Vec<MarkupKind>>,
 }
 
 impl State {
@@ -265,6 +266,7 @@ impl State {
             loggingFile: None,
             loggingLevel: log::LevelFilter::Warn,
             serverStderr: None,
+            preferred_markup_kind: None,
 
             logger,
         })


Test patch: