From 9e02cfab93b523027c59175621a0fa4a45f5e065 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Tue, 19 Sep 2023 04:09:04 +0200 Subject: [PATCH 1/3] Do not echo diagnostics in virtual-text when update_diagnostics_in_insert_mode == 0 Needed settings: let g:ycm_update_diagnostics_in_insert_mode = 0 let g:ycm_echo_current_diagnostic = 'virtual-text' Previously, whenever we should NOT display diagnostic UI, but diag message would not need clearing, we ended up echoing. Most noticable in the following example int main() { puts("") } Note that this first patch still leaves a diagnostic UI flicker on InsertLeave. --- python/ycm/diagnostic_interface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ycm/diagnostic_interface.py b/python/ycm/diagnostic_interface.py index 9a81d7cf83..4876fa67fb 100644 --- a/python/ycm/diagnostic_interface.py +++ b/python/ycm/diagnostic_interface.py @@ -44,8 +44,7 @@ def OnCursorMoved( self ): if self._user_options[ 'echo_current_diagnostic' ]: line, _ = vimsupport.CurrentLineAndColumn() line += 1 # Convert to 1-based - if ( not self.ShouldUpdateDiagnosticsUINow() and - self._diag_message_needs_clearing ): + if not self.ShouldUpdateDiagnosticsUINow(): # Clear any previously echo'd diagnostic in insert mode self._EchoDiagnosticText( line, None, None ) elif line != self._previous_diag_line_number: From 1d15b86c9d7662720d9042596a1ad252e526713b Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Tue, 19 Sep 2023 04:15:56 +0200 Subject: [PATCH 2/3] Make sure to refresh diagnostic UI on InsertLeave In case the current filetype language server uses LSP async diagnostics, we might not get a reparse on InsertLeave. This happens if the user leaves insert mode after a semantic trigger. We would still try sending a FileReadyToParse request, but the diags get ignored, because: a) The filetype is known to use async diags. b) We do not insist on a synchronous diag update, like :YcmDiags. The solution is to check if the current filetype uses async diagnostics and, if so, let InsertLeave refresh diagnostic UI regardless of what the state of FileReadyToParse request is. Note that this solution makes the flicker on leaving insert mode harder to fix. Not only does OnCursorMoved cause a refresh of stale diagnostics, but so does OnInsertLeave. --- python/ycm/youcompleteme.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index d7eb18862b..a09b2ef23a 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -614,8 +614,10 @@ def OnInsertEnter( self ): def OnInsertLeave( self ): + async_diags = any( self._message_poll_requests.get( filetype ) + for filetype in vimsupport.CurrentFiletypes() ) if ( not self._user_options[ 'update_diagnostics_in_insert_mode' ] and - not self.CurrentBuffer().ParseRequestPending() ): + ( async_diags or not self.CurrentBuffer().ParseRequestPending() ) ): self.CurrentBuffer().RefreshDiagnosticsUI() SendEventNotificationAsync( 'InsertLeave' ) From 456b469d1fa89a2260ee597073196b3db8062f5f Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Fri, 6 Oct 2023 04:19:21 +0200 Subject: [PATCH 3/3] Add tests for diagnostics clearing in insert mode --- test/diagnostics.test.vim | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/test/diagnostics.test.vim b/test/diagnostics.test.vim index 2359670c96..b8a60f9ed5 100644 --- a/test/diagnostics.test.vim +++ b/test/diagnostics.test.vim @@ -52,7 +52,7 @@ function! Test_Disable_Diagnostics_Update_In_insert_Mode() " Must do the checks in a timer callback because we need to stay in insert " mode until done. - function! Check( id ) closure + function! CheckNoDiagUIAfterOpenParenthesis( id ) closure call WaitForAssert( {-> \ assert_true( \ py3eval( @@ -62,10 +62,11 @@ function! Test_Disable_Diagnostics_Update_In_insert_Mode() \ '%', \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } ) - call FeedAndCheckAgain( " \\\", funcref( 'CheckAgain' ) ) + call FeedAndCheckAgain( " \\\)", + \ funcref( 'CheckNoDiagUIAfterClosingPatenthesis' ) ) endfunction - function! CheckAgain( id ) closure + function! CheckNoDiagUIAfterClosingPatenthesis( id ) closure call WaitForAssert( {-> \ assert_true( \ py3eval( @@ -76,9 +77,37 @@ function! Test_Disable_Diagnostics_Update_In_insert_Mode() \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } ) call feedkeys( "\" ) + call FeedAndCheckAgain( "\", + \ funcref( 'CheckDiagUIRefreshedAfterLeavingInsertMode' ) ) endfunction - call FeedAndCheckMain( 'imain(', funcref( 'Check' ) ) + function! CheckDiagUIRefreshedAfterLeavingInsertMode( id ) closure + call WaitForAssert( {-> + \ assert_true( + \ py3eval( + \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )' + \ ) ) } ) + call WaitForAssert( {-> assert_true( len( sign_getplaced( + \ '%', + \ { 'group': 'ycm_signs' } )[ 0 ][ 'signs' ] ) ) } ) + call FeedAndCheckAgain( "A\", funcref( 'CheckNoPropsAfterNewLine' ) + endfunction + + function! CheckNoPropsAfterNewLine( id ) closure + call WaitForAssert( {-> + \ assert_true( + \ py3eval( + \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )' + \ ) ) } ) + call WaitForAssert( {-> assert_false( len( prop_list( + \ 1, { 'end_lnum': -1, + \ 'types': [ 'YcmVirtDiagWarning', + \ 'YcmVirtDiagError', + \ 'YcmVirtDiagPadding' ] } ) ) ) ) + endfunction + + call FeedAndCheckMain( 'imain(', + \ funcref( 'CheckNoDiagUIAfterOpenParenthesis' ) ) call test_override( 'ALL', 0 ) endfunction