Browse Source

Update based on the latest upstream changes.

Davit Samvelyan 7 years ago
parent
commit
3a690fc8d4

+ 1 - 3
autoload/youcompleteme.vim

@@ -479,15 +479,13 @@ function! s:OnFileReadyToParse( ... )
 
   " We only want to send a new FileReadyToParse event notification if the buffer
   " has changed since the last time we sent one, or if forced.
-  if force_parsing || b:changedtick != get( b:, 'ycm_changedtick', -1 )
+  if force_parsing || s:Pyeval( "ycm_state.NeedsReparse()" )
     exec s:python_command "ycm_state.OnFileReadyToParse()"
 
     call timer_stop( s:pollers.file_parse_response.id )
     let s:pollers.file_parse_response.id = timer_start(
           \ s:pollers.file_parse_response.wait_milliseconds,
           \ function( 's:PollFileParseResponse' ) )
-
-    let b:ycm_changedtick = b:changedtick
   endif
 endfunction
 

+ 7 - 3
python/ycm/buffer.py

@@ -38,13 +38,17 @@ class Buffer( object ):
 
 
   def FileParseRequestReady( self, block = False ):
-    return self._parse_tick == 0 or block or self._parse_request.Done()
+    return bool( self._parse_request and
+                 ( block or self._parse_request.Done() ) )
 
 
   def SendParseRequest( self, extra_data ):
     self._parse_request = EventNotification( 'FileReadyToParse',
                                              extra_data = extra_data )
     self._parse_request.Start()
+    # Decrement handled tick to ensure correct handling when we are forcing
+    # reparse on buffer visit and changed tick remains the same.
+    self._handled_tick -= 1
     self._parse_tick = self._ChangedTick()
 
 
@@ -53,8 +57,8 @@ class Buffer( object ):
 
 
   def UpdateDiagnostics( self ):
-    diagnostics = self._parse_request.Response()
-    self._diag_interface.UpdateWithNewDiagnostics( diagnostics )
+    self._diag_interface.UpdateWithNewDiagnostics(
+      self._parse_request.Response() )
 
 
   def PopulateLocationList( self ):

+ 57 - 60
python/ycm/tests/event_notification_test.py

@@ -25,7 +25,6 @@ from __future__ import absolute_import
 from builtins import *  # noqa
 
 from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
-                                   EmulateCurrentBufferChange,
                                    MockVimBuffers, MockVimModule, VimBuffer )
 MockVimModule()
 
@@ -106,7 +105,7 @@ def MockEventNotification( response_method, native_filetype_completer = True ):
         'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype',
         return_value = native_filetype_completer ):
 
-        with patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReady',
+        with patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
                     return_value = True ):
 
           yield
@@ -142,7 +141,6 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test(
         call( ERROR_TEXT, truncate = True )
       ] )
 
-      EmulateCurrentBufferChange()
       # But it does if a subsequent event raises again
       ycm.OnFileReadyToParse()
       ok_( ycm.FileParseRequestReady() )
@@ -212,7 +210,6 @@ def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
           call( FILE_NAME ),
         ] )
 
-        EmulateCurrentBufferChange()
         # But it does if a subsequent event raises again
         ycm.OnFileReadyToParse()
         ok_( ycm.FileParseRequestReady() )
@@ -227,7 +224,6 @@ def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
           call( FILE_NAME ),
         ] )
 
-      EmulateCurrentBufferChange()
       # When the user rejects the extra conf, we reject it
       with patch( 'ycm.vimsupport.PresentDialog',
                   return_value = 1,
@@ -253,7 +249,6 @@ def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
           call( FILE_NAME ),
         ] )
 
-        EmulateCurrentBufferChange()
         # But it does if a subsequent event raises again
         ycm.OnFileReadyToParse()
         ok_( ycm.FileParseRequestReady() )
@@ -271,12 +266,9 @@ def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
 
 @YouCompleteMeInstance()
 def EventNotification_FileReadyToParse_Diagnostic_Error_Native_test( ycm ):
-  with MockArbitraryBuffer( 'cpp' ):
-    _Check_FileReadyToParse_Diagnostic_Error( ycm )
-    EmulateCurrentBufferChange()
-    _Check_FileReadyToParse_Diagnostic_Warning( ycm )
-    EmulateCurrentBufferChange()
-    _Check_FileReadyToParse_Diagnostic_Clean( ycm )
+  _Check_FileReadyToParse_Diagnostic_Error( ycm )
+  _Check_FileReadyToParse_Diagnostic_Warning( ycm )
+  _Check_FileReadyToParse_Diagnostic_Clean( ycm )
 
 
 @patch( 'vim.command' )
@@ -290,24 +282,24 @@ def _Check_FileReadyToParse_Diagnostic_Error( ycm, vim_command ):
     diagnostic = Diagnostic( [], start, extent, 'expected ;', 'ERROR' )
     return [ BuildDiagnosticData( diagnostic ) ]
 
-  with MockEventNotification( DiagnosticResponse ):
-    ycm.OnFileReadyToParse()
-    ok_( ycm.FileParseRequestReady() )
-    ycm.HandleFileParseRequest()
-    vim_command.assert_has_calls( [
-      PlaceSign_Call( 1, 1, 1, True )
-    ] )
-    eq_( ycm.GetErrorCount(), 1 )
-    eq_( ycm.GetWarningCount(), 0 )
-
-    # Consequent calls to HandleFileParseRequest shouldn't mess with
-    # existing diagnostics, when there is no new parse request.
-    vim_command.reset_mock()
-    ok_( ycm.FileParseRequestReady() )
-    ycm.HandleFileParseRequest()
-    vim_command.assert_not_called()
-    eq_( ycm.GetErrorCount(), 1 )
-    eq_( ycm.GetWarningCount(), 0 )
+  with MockArbitraryBuffer( 'cpp' ):
+    with MockEventNotification( DiagnosticResponse ):
+      ycm.OnFileReadyToParse()
+      ok_( ycm.FileParseRequestReady() )
+      ycm.HandleFileParseRequest()
+      vim_command.assert_has_calls( [
+        PlaceSign_Call( 1, 1, 1, True )
+      ] )
+      eq_( ycm.GetErrorCount(), 1 )
+      eq_( ycm.GetWarningCount(), 0 )
+
+      # Consequent calls to HandleFileParseRequest shouldn't mess with
+      # existing diagnostics, when there is no new parse request.
+      vim_command.reset_mock()
+      ycm.HandleFileParseRequest()
+      vim_command.assert_not_called()
+      eq_( ycm.GetErrorCount(), 1 )
+      eq_( ycm.GetWarningCount(), 0 )
 
 
 @patch( 'vim.command' )
@@ -322,25 +314,25 @@ def _Check_FileReadyToParse_Diagnostic_Warning( ycm, vim_command ):
     diagnostic = Diagnostic( [], start, extent, 'cast', 'WARNING' )
     return [ BuildDiagnosticData( diagnostic ) ]
 
-  with MockEventNotification( DiagnosticResponse ):
-    ycm.OnFileReadyToParse()
-    ok_( ycm.FileParseRequestReady() )
-    ycm.HandleFileParseRequest()
-    vim_command.assert_has_calls( [
-      PlaceSign_Call( 2, 2, 1, False ),
-      UnplaceSign_Call( 1, 1 )
-    ] )
-    eq_( ycm.GetErrorCount(), 0 )
-    eq_( ycm.GetWarningCount(), 1 )
-
-    # Consequent calls to HandleFileParseRequest shouldn't mess with
-    # existing diagnostics, when there is no new parse request.
-    vim_command.reset_mock()
-    ok_( ycm.FileParseRequestReady() )
-    ycm.HandleFileParseRequest()
-    vim_command.assert_not_called()
-    eq_( ycm.GetErrorCount(), 0 )
-    eq_( ycm.GetWarningCount(), 1 )
+  with MockArbitraryBuffer( 'cpp' ):
+    with MockEventNotification( DiagnosticResponse ):
+      ycm.OnFileReadyToParse()
+      ok_( ycm.FileParseRequestReady() )
+      ycm.HandleFileParseRequest()
+      vim_command.assert_has_calls( [
+        PlaceSign_Call( 2, 2, 1, False ),
+        UnplaceSign_Call( 1, 1 )
+      ] )
+      eq_( ycm.GetErrorCount(), 0 )
+      eq_( ycm.GetWarningCount(), 1 )
+
+      # Consequent calls to HandleFileParseRequest shouldn't mess with
+      # existing diagnostics, when there is no new parse request.
+      vim_command.reset_mock()
+      ycm.HandleFileParseRequest()
+      vim_command.assert_not_called()
+      eq_( ycm.GetErrorCount(), 0 )
+      eq_( ycm.GetWarningCount(), 1 )
 
 
 @patch( 'vim.command' )
@@ -348,17 +340,20 @@ def _Check_FileReadyToParse_Diagnostic_Clean( ycm, vim_command ):
   # Tests Vim sign unplacement and error/warning count python API
   # when there are no errors/warnings left.
   # Should be called after _Check_FileReadyToParse_Diagnostic_Warning
-  with MockEventNotification( MagicMock( return_value = [] ) ):
-    ycm.OnFileReadyToParse()
-    ycm.HandleFileParseRequest()
-    vim_command.assert_has_calls( [
-      UnplaceSign_Call( 2, 1 )
-    ] )
-    eq_( ycm.GetErrorCount(), 0 )
-    eq_( ycm.GetWarningCount(), 0 )
+  with MockArbitraryBuffer( 'cpp' ):
+    with MockEventNotification( MagicMock( return_value = [] ) ):
+      ycm.OnFileReadyToParse()
+      ycm.HandleFileParseRequest()
+      vim_command.assert_has_calls( [
+        UnplaceSign_Call( 2, 1 )
+      ] )
+      eq_( ycm.GetErrorCount(), 0 )
+      eq_( ycm.GetWarningCount(), 0 )
 
 
 @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @YouCompleteMeInstance( { 'collect_identifiers_from_tags_files': 1 } )
 def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test(
     ycm, *args ):
@@ -372,9 +367,7 @@ def EventNotification_FileReadyToParse_TagFiles_UnicodeWorkingDirectory_test(
               'PostDataToHandlerAsync' ) as post_data_to_handler_async:
     with CurrentWorkingDirectory( unicode_dir ):
       with MockVimBuffers( [ current_buffer ], current_buffer, ( 6, 5 ) ):
-        with patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReady',
-                    return_value = True ):
-          ycm.OnFileReadyToParse()
+        ycm.OnFileReadyToParse()
 
     assert_that(
       # Positional arguments passed to PostDataToHandlerAsync.
@@ -505,6 +498,8 @@ def EventNotification_BufferUnload_BuildRequestForDeletedAndUnsavedBuffers_test(
 
 @patch( 'ycm.syntax_parse.SyntaxKeywordsForCurrentBuffer',
         return_value = [ 'foo', 'bar' ] )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @YouCompleteMeInstance( { 'seed_identifiers_with_syntax': 1 } )
 def EventNotification_FileReadyToParse_SyntaxKeywords_SeedWithCache_test(
     ycm, *args ):
@@ -539,6 +534,8 @@ def EventNotification_FileReadyToParse_SyntaxKeywords_SeedWithCache_test(
 
 @patch( 'ycm.syntax_parse.SyntaxKeywordsForCurrentBuffer',
         return_value = [ 'foo', 'bar' ] )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @YouCompleteMeInstance( { 'seed_identifiers_with_syntax': 1 } )
 def EventNotification_FileReadyToParse_SyntaxKeywords_ClearCacheIfRestart_test(
     ycm, *args ):

+ 8 - 4
python/ycm/tests/youcompleteme_test.py

@@ -349,7 +349,8 @@ def YouCompleteMe_ShowDiagnostics_NoDiagnosticsDetected_test(
                           'open_loclist_on_ycm_diags': 0 } )
 @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype',
         return_value = True )
-@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReady', return_value = True )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
 @patch( 'ycm.vimsupport.SetLocationList', new_callable = ExtendedMock )
 def YouCompleteMe_ShowDiagnostics_DiagnosticsFound_DoNotOpenLocationList_test(
@@ -389,7 +390,8 @@ def YouCompleteMe_ShowDiagnostics_DiagnosticsFound_DoNotOpenLocationList_test(
 @YouCompleteMeInstance( { 'open_loclist_on_ycm_diags': 1 } )
 @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype',
         return_value = True )
-@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReady', return_value = True )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
 @patch( 'ycm.vimsupport.SetLocationList', new_callable = ExtendedMock )
 @patch( 'ycm.vimsupport.OpenLocationList', new_callable = ExtendedMock )
@@ -433,7 +435,8 @@ def YouCompleteMe_ShowDiagnostics_DiagnosticsFound_OpenLocationList_test(
                           'enable_diagnostic_highlighting': 1 } )
 @patch( 'ycm.youcompleteme.YouCompleteMe.FiletypeCompleterExistsForFiletype',
         return_value = True )
-@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReady', return_value = True )
+@patch( 'ycm.youcompleteme.YouCompleteMe.IsServerReadyWithCache',
+        return_value = True )
 @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
 @patch( 'vim.command', new_callable = ExtendedMock )
 def YouCompleteMe_UpdateDiagnosticInterface_PrioritizeErrorsOverWarnings_test(
@@ -514,7 +517,8 @@ def YouCompleteMe_UpdateDiagnosticInterface_PrioritizeErrorsOverWarnings_test(
   with MockVimBuffers( [ current_buffer ], current_buffer, ( 3, 1 ) ):
     with patch( 'ycm.client.event_notification.EventNotification.Response',
                 return_value = diagnostics ):
-      ycm.OnFileReadyToParse( block = True )
+      ycm.OnFileReadyToParse()
+      ycm.HandleFileParseRequest( block = True )
 
     # Error match is added after warning matches.
     assert_that(

+ 5 - 0
python/ycm/vimsupport.py

@@ -592,6 +592,11 @@ def CurrentFiletypes():
   return VimExpressionToPythonType( "&filetype" ).split( '.' )
 
 
+def GetBufferFiletypes( bufnr ):
+  command = 'getbufvar({0}, "&ft")'.format( bufnr )
+  return VimExpressionToPythonType( command ).split( '.' )
+
+
 def FiletypesForBuffer( buffer_object ):
   # NOTE: Getting &ft for other buffers only works when the buffer has been
   # visited by the user at least once, which is true for modified buffers

+ 49 - 23
python/ycm/youcompleteme.py

@@ -232,6 +232,10 @@ class YouCompleteMe( object ):
     return self._server_is_ready_with_cache
 
 
+  def IsServerReadyWithCache( self ):
+    return self._server_is_ready_with_cache
+
+
   def _NotifyUserIfServerCrashed( self ):
     if self._user_notified_about_crash or self.IsServerAlive():
       return
@@ -355,28 +359,26 @@ class YouCompleteMe( object ):
              self.NativeFiletypeCompletionAvailable() )
 
 
-  def OnFileReadyToParse( self, block = False, force = False ):
+  def NeedsReparse( self ):
+    return self._GetCurrentBuffer().NeedsReparse()
+
+
+  def OnFileReadyToParse( self ):
     if not self.IsServerAlive():
       self._NotifyUserIfServerCrashed()
       return
 
-    if not self.IsServerReady():
+    if not self.IsServerReadyWithCache():
       return
 
     self._omnicomp.OnFileReadyToParse( None )
 
-    self.HandleFileParseRequest()
-
-    current_buffer = self._GetCurrentBuffer()
-    if force_parsing or current_buffer.NeedsReparse():
-      extra_data = {}
-      self._AddTagsFilesIfNeeded( extra_data )
-      self._AddSyntaxDataIfNeeded( extra_data )
-      self._AddExtraConfDataIfNeeded( extra_data )
+    extra_data = {}
+    self._AddTagsFilesIfNeeded( extra_data )
+    self._AddSyntaxDataIfNeeded( extra_data )
+    self._AddExtraConfDataIfNeeded( extra_data )
 
-      current_buffer.SendParseRequest( extra_data )
-      if block:
-        self.HandleFileParseRequest()
+    self._GetCurrentBuffer().SendParseRequest( extra_data )
 
 
   def OnBufferUnload( self, deleted_buffer_file ):
@@ -543,21 +545,44 @@ class YouCompleteMe( object ):
 
 
   def FileParseRequestReady( self ):
-    return self._GetCurrentBuffer().FileParseRequestReady()
+    # Return True if server is not ready yet, to stop repeating check timer.
+    return ( not self.IsServerReadyWithCache() or
+             self._GetCurrentBuffer().FileParseRequestReady() )
 
 
-  def HandleFileParseRequest( self ):
-    current_buffer = self._GetCurrentBuffer()
-    if current_buffer.IsResponseHandled():
+  def HandleFileParseRequest( self, block = False ):
+    if not self.IsServerReadyWithCache():
       return
 
+    current_buffer = self._GetCurrentBuffer()
+    # Order is important here:
+    # FileParseRequestReady has a low cost, while
     # NativeFiletypeCompletionUsable is a blocking server request
-    if self.NativeFiletypeCompletionUsable():
-      current_buffer.GetResponse()
+    if ( not current_buffer.IsResponseHandled() and
+         current_buffer.FileParseRequestReady( block ) and
+         self.NativeFiletypeCompletionUsable() ):
+
       if self.ShouldDisplayDiagnostics():
         current_buffer.UpdateDiagnostics()
-
-    current_buffer.MarkResponseHandled()
+      else:
+        # YCM client has a hard-coded list of filetypes which are known
+        # to support diagnostics, self.DiagnosticUiSupportedForCurrentFiletype()
+        #
+        # For filetypes which don't support diagnostics, we just want to check
+        # the _latest_file_parse_request for any exception or UnknownExtraConf
+        # response, to allow the server to raise configuration warnings, etc.
+        # to the user. We ignore any other supplied data.
+        current_buffer.GetResponse()
+
+      # We set the file parse request as handled because we want to prevent
+      # repeated issuing of the same warnings/errors/prompts. Setting this
+      # makes IsRequestHandled return True until the next request is created.
+      #
+      # Note: it is the server's responsibility to determine the frequency of
+      # error/warning/prompts when receiving a FileReadyToParse event, but
+      # it our responsibility to ensure that we only apply the
+      # warning/error/prompt received once (for each event).
+      current_buffer.MarkResponseHandled()
 
 
   def DebugInfo( self ):
@@ -666,7 +691,8 @@ class YouCompleteMe( object ):
     vimsupport.PostVimMessage(
         'Forcing compilation, this will block Vim until done.',
         warning = False )
-    self.OnFileReadyToParse( block = True )
+    self.OnFileReadyToParse()
+    self.HandleFileParseRequest( block = True )
     vimsupport.PostVimMessage( 'Diagnostics refreshed', warning = False )
     return True
 
@@ -691,7 +717,7 @@ class YouCompleteMe( object ):
     if filetype in self._filetypes_with_keywords_loaded:
       return
 
-    if self.IsServerReady():
+    if self.IsServerReadyWithCache():
       self._filetypes_with_keywords_loaded.add( filetype )
     extra_data[ 'syntax_keywords' ] = list(
        syntax_parse.SyntaxKeywordsForCurrentBuffer() )