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

Issue:
Add hideVirtualTextsOnInsert option
This pull request fixes #816 by adding an option (`LanguageClient_hideVirtualTextsOnInsert`) to hide diagnostics in virtual texts while typing (insert mode) and setting `1` as a default value for it.

Here's what the end result looks like:

![recording-2019-09-26_07 57 56](https://user-images.githubusercontent.com/4250565/65683522-c71c8600-e033-11e9-91fc-51aab3403cfb.gif)


Feature Request: an option to disable notifications in insert mode
## Is your feature request related to a problem? Please describe.
I am disturbed by the error messages and hints when I'm editing my code and not finished yet.

## Describe the solution you'd like
Provide an option to disable these notifications in insert mode, and when I'm switching to normal mode, display them again.

## Describe alternatives you've considered
Consider to make the above being default behavior.
feat: add hideVirtualTextsOnInsert option
@martskins this still does not compile unfortunately...

Adds option to hide diagnostics in virtual texts while typing.

Closes #816


Discussion / hints:
On thing to note is that this causes the diagnostics not to show in virtual texts from the moment you stop editing and exit insert mode until the moment you move the cursor. This is because the call to `set_virtual_texts` in `didChange` clears the virtual texts but only a `handleCursorMoved` places them back, as `set_virtual_texts` is not called in `didSave`.

How do you feel about adding a call to `set_virtual_texts` in `didSave`?
Closing in favor of #982
I would also like to see that. This behaviour can be seen in YouCompleteMe, for example.
There, the validation wouldn't trigger until returned into the normal mode after some change is made. That way we wouldn't get false-positive errors during typing of our code :)
Are we talking about the diagnostics shown in virtual text here?
If that's the case I also found that a little distracting while typing, so I've been looking at LanguageClient-neovim's source code and I think I found a way to hide all virtual texts for the buffer you are editing while in insert mode.
Should I open a PR? Or are there any other plans for this?
See #982

Fix patch:
diff --git a/doc/LanguageClient.txt b/doc/LanguageClient.txt
index 70a3878e..271e4594 100644
--- a/doc/LanguageClient.txt
+++ b/doc/LanguageClient.txt
@@ -542,13 +542,20 @@ This setting may have no effect of the server decides not to honour it.
 Default: v:null
 Valid options: Array<String>
 
-2.34 g:LanguageClient_floatingWindowStyle  *g:LanguageClient_floatingWindowStyle*
+2.35 g:LanguageClient_floatingWindowStyle  *g:LanguageClient_floatingWindowStyle*
 
 Style of opened Neovim floating window.
 
 Default: 'minimal'
 Valid options: 'minimal'
 
+2.36 g:LanguageClient_hideVirtualTextsOnInsert *g:LanguageClient_hideVirtualTextsOnInsert*
+
+Hides all virtual texts for the current buffer while editing in insert mode.
+
+Default: 0
+Valid options: 1 | 0
+
 ==============================================================================
 3. Commands                                           *LanguageClientCommands*
 
diff --git a/src/language_server_protocol.rs b/src/language_server_protocol.rs
index 3ddbdfb2..657d2a06 100644
--- a/src/language_server_protocol.rs
+++ b/src/language_server_protocol.rs
@@ -1,5 +1,5 @@
 use crate::language_client::LanguageClient;
-use crate::vim::try_get;
+use crate::vim::{try_get, Mode};
 use crate::{
     rpcclient::RpcClient,
     types::*,
@@ -172,6 +172,7 @@ impl LanguageClient {
             semantic_scope_separator,
             apply_completion_text_edits,
             preferred_markup_kind,
+            hide_virtual_texts_on_insert,
         ): (
             Option<usize>,
             String,
@@ -183,6 +184,7 @@ impl LanguageClient {
             String,
             u8,
             Option<Vec<MarkupKind>>,
+            u8,
         ) = self.vim()?.eval(
             [
                 "get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)",
@@ -195,6 +197,7 @@ impl LanguageClient {
                 "s:GetVar('LanguageClient_semanticScopeSeparator', ':')",
                 "get(g:, 'LanguageClient_applyCompletionAdditionalTextEdits', 1)",
                 "get(g:, 'LanguageClient_preferredMarkupKind', v:null)",
+                "s:GetVar('LanguageClient_hideVirtualTextsOnInsert', 0)",
             ]
             .as_ref(),
         )?;
@@ -301,6 +304,7 @@ impl LanguageClient {
             state.completion_prefer_text_edit = prefer_text_edit;
             state.apply_completion_additional_text_edits = apply_completion_edits;
             state.use_virtual_text = use_virtual_text;
+            state.hide_virtual_texts_on_insert = hide_virtual_texts_on_insert == 1;
             state.echo_project_root = echo_project_root == 1;
             state.server_stderr = server_stderr;
             state.is_nvim = is_nvim;
@@ -2249,9 +2253,7 @@ impl LanguageClient {
             }
         }
 
-        let bufnr = self.vim()?.get_bufnr(&filename, params)?;
-        let viewport = self.vim()?.get_viewport(params)?;
-        self.draw_virtual_texts(&filename, viewport, bufnr)?;
+        self.draw_virtual_texts(&params)?;
 
         Ok(Value::Null)
     }
@@ -2391,6 +2393,8 @@ impl LanguageClient {
             },
         )?;
 
+        self.draw_virtual_texts(params)?;
+
         info!(
             "End {}",
             lsp_types::notification::DidSaveTextDocument::METHOD
@@ -3313,19 +3317,34 @@ impl LanguageClient {
                 .notify("s:AddHighlights", json!([source, highlights]))?;
         }
 
-        let bufnr = self.vim()?.get_bufnr(&filename, params)?;
-        self.draw_virtual_texts(&filename, viewport, bufnr)?;
+        self.draw_virtual_texts(&params)?;
 
         info!("End {}", NOTIFICATION_HANDLE_CURSOR_MOVED);
         Ok(())
     }
 
-    fn draw_virtual_texts(
-        &self,
-        filename: &str,
-        viewport: viewport::Viewport,
-        bufnr: i64,
-    ) -> Fallible<()> {
+    fn draw_virtual_texts(&self, params: &Value) -> Fallible<()> {
+        if !self.get(|state| state.is_nvim)? {
+            return Ok(());
+        }
+
+        let filename = self.vim()?.get_filename(params)?;
+        let filename = filename.as_str();
+        let viewport = self.vim()?.get_viewport(params)?;
+        let bufnr = self.vim()?.get_bufnr(&filename, params)?;
+        let namespace_id = self.get_or_create_namespace(&LCNamespace::VirtualText)?;
+        let is_insert_mode = self.vim()?.get_mode()? == Mode::Insert;
+        if self.get(|state| state.hide_virtual_texts_on_insert)? && is_insert_mode {
+            self.vim()?.set_virtual_texts(
+                bufnr,
+                namespace_id,
+                viewport.start,
+                viewport.end,
+                &[],
+            )?;
+            return Ok(());
+        }
+
         let mut virtual_texts = vec![];
         let use_virtual_text = self.get(|state| state.use_virtual_text.clone())?;
 
@@ -3344,16 +3363,13 @@ impl LanguageClient {
             virtual_texts.extend(self.virtual_texts_from_code_lenses(filename)?.into_iter());
         }
 
-        if self.get(|state| state.is_nvim)? {
-            let namespace_id = self.get_or_create_namespace(&LCNamespace::VirtualText)?;
-            self.vim()?.set_virtual_texts(
-                bufnr,
-                namespace_id,
-                viewport.start,
-                viewport.end,
-                &virtual_texts,
-            )?;
-        }
+        self.vim()?.set_virtual_texts(
+            bufnr,
+            namespace_id,
+            viewport.start,
+            viewport.end,
+            &virtual_texts,
+        )?;
 
         Ok(())
     }
diff --git a/src/types.rs b/src/types.rs
index c7b68c72..59ae7b88 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -200,6 +200,7 @@ pub struct State {
     pub completion_prefer_text_edit: bool,
     pub apply_completion_additional_text_edits: bool,
     pub use_virtual_text: UseVirtualText,
+    pub hide_virtual_texts_on_insert: bool,
     pub echo_project_root: bool,
 
     pub server_stderr: Option<String>,
@@ -279,6 +280,7 @@ impl State {
             completion_prefer_text_edit: false,
             apply_completion_additional_text_edits: true,
             use_virtual_text: UseVirtualText::All,
+            hide_virtual_texts_on_insert: true,
             echo_project_root: true,
             server_stderr: None,
             preferred_markup_kind: None,
diff --git a/src/vim.rs b/src/vim.rs
index 6be4b35f..221822fe 100644
--- a/src/vim.rs
+++ b/src/vim.rs
@@ -23,6 +23,43 @@ pub fn try_get<R: DeserializeOwned>(key: &str, params: &Value) -> Fallible<Optio
     }
 }
 
+#[derive(PartialEq)]
+pub enum Mode {
+    Normal,
+    Insert,
+    Replace,
+    Visual,
+    VisualLine,
+    VisualBlock,
+    Command,
+    Select,
+    SelectLine,
+    SelectBlock,
+    Terminal,
+}
+
+impl From<&str> for Mode {
+    fn from(mode: &str) -> Self {
+        match mode {
+            "n" => Mode::Normal,
+            "i" => Mode::Insert,
+            "R" => Mode::Replace,
+            "v" => Mode::Visual,
+            "V" => Mode::VisualLine,
+            "<C-v>" => Mode::VisualBlock,
+            "c" => Mode::Command,
+            "s" => Mode::Select,
+            "S" => Mode::SelectLine,
+            "<C-s>" => Mode::SelectBlock,
+            "t" => Mode::Terminal,
+            m => {
+                error!("unknown mode {}, falling back to Mode::Normal", m);
+                Mode::Normal
+            }
+        }
+    }
+}
+
 #[derive(Clone)]
 pub struct Vim {
     pub rpcclient: Arc<RpcClient>,
@@ -35,6 +72,11 @@ impl Vim {
 
     /// Fundamental functions.
 
+    pub fn get_mode(&self) -> Fallible<Mode> {
+        let mode: String = self.rpcclient.call("mode", json!([]))?;
+        Ok(Mode::from(mode.as_str()))
+    }
+
     pub fn command(&self, cmds: impl Serialize) -> Fallible<()> {
         self.rpcclient.notify("s:command", &cmds)
     }


Test patch: