diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index a1f10ae6..32cd3ce7 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -565,6 +565,9 @@ function! s:PollFileParseResponse( ... ) endif exec s:python_command "ycm_state.HandleFileParseRequest()" + if s:Pyeval( "ycm_state.ShouldResendFileParseRequest()" ) + call s:OnFileReadyToParse( 1 ) + endif endfunction diff --git a/python/ycm/buffer.py b/python/ycm/buffer.py index 321699f9..3bed55e1 100644 --- a/python/ycm/buffer.py +++ b/python/ycm/buffer.py @@ -66,6 +66,10 @@ class Buffer( object ): return self._parse_tick != self._ChangedTick() + def ShouldResendParseRequest( self ): + return self._parse_request.ShouldResend() + + def UpdateDiagnostics( self, force=False ): if force or not self._async_diags: self.UpdateWithNewDiagnostics( self._parse_request.Response() ) diff --git a/python/ycm/client/base_request.py b/python/ycm/client/base_request.py index 05949d84..10fb7992 100644 --- a/python/ycm/client/base_request.py +++ b/python/ycm/client/base_request.py @@ -43,7 +43,7 @@ _logger = logging.getLogger( __name__ ) class BaseRequest( object ): def __init__( self ): - pass + self._should_resend = False def Start( self ): @@ -58,15 +58,22 @@ class BaseRequest( object ): return {} - @staticmethod - def HandleFuture( future, display_message = True, truncate_message = False ): + def ShouldResend( self ): + return self._should_resend + + + def HandleFuture( self, + future, + display_message = True, + truncate_message = False ): """Get the server response from a |future| object and catch any exception while doing so. If an exception is raised because of a unknown .ycm_extra_conf.py file, load the file or ignore it after asking the user. - For other exceptions, log the exception and display its message to the user - on the Vim status line. Unset the |display_message| parameter to hide the - message from the user. Set the |truncate_message| parameter to avoid - hit-enter prompts from this message.""" + An identical request should be sent again to the server. For other + exceptions, log the exception and display its message to the user on the Vim + status line. Unset the |display_message| parameter to hide the message from + the user. Set the |truncate_message| parameter to avoid hit-enter prompts + from this message.""" try: try: return _JsonFromFuture( future ) @@ -75,6 +82,7 @@ class BaseRequest( object ): _LoadExtraConfFile( e.extra_conf_file ) else: _IgnoreExtraConfFile( e.extra_conf_file ) + self._should_resend = True except BaseRequest.Requests().exceptions.ConnectionError: # We don't display this exception to the user since it is likely to happen # for each subsequent request (typically if the server crashed) and we @@ -93,12 +101,12 @@ class BaseRequest( object ): # up; see Requests docs for details (we just pass the param along). # See the HandleFuture method for the |display_message| and |truncate_message| # parameters. - @staticmethod - def GetDataFromHandler( handler, + def GetDataFromHandler( self, + handler, timeout = _READ_TIMEOUT_SEC, display_message = True, truncate_message = False ): - return BaseRequest.HandleFuture( + return self.HandleFuture( BaseRequest._TalkToHandlerAsync( '', handler, 'GET', timeout ), display_message, truncate_message ) @@ -109,13 +117,13 @@ class BaseRequest( object ): # up; see Requests docs for details (we just pass the param along). # See the HandleFuture method for the |display_message| and |truncate_message| # parameters. - @staticmethod - def PostDataToHandler( data, + def PostDataToHandler( self, + data, handler, timeout = _READ_TIMEOUT_SEC, display_message = True, truncate_message = False ): - return BaseRequest.HandleFuture( + return self.HandleFuture( BaseRequest.PostDataToHandlerAsync( data, handler, timeout ), display_message, truncate_message ) @@ -243,13 +251,13 @@ def _JsonFromFuture( future ): def _LoadExtraConfFile( filepath ): - BaseRequest.PostDataToHandler( { 'filepath': filepath }, - 'load_extra_conf_file' ) + BaseRequest().PostDataToHandler( { 'filepath': filepath }, + 'load_extra_conf_file' ) def _IgnoreExtraConfFile( filepath ): - BaseRequest.PostDataToHandler( { 'filepath': filepath }, - 'ignore_extra_conf_file' ) + BaseRequest().PostDataToHandler( { 'filepath': filepath }, + 'ignore_extra_conf_file' ) def DisplayServerException( exception, truncate_message = False ): diff --git a/python/ycm/client/ycmd_keepalive.py b/python/ycm/client/ycmd_keepalive.py index 3e148505..278ef86e 100644 --- a/python/ycm/client/ycmd_keepalive.py +++ b/python/ycm/client/ycmd_keepalive.py @@ -45,4 +45,4 @@ class YcmdKeepalive( object ): while True: time.sleep( self._ping_interval_seconds ) - BaseRequest.GetDataFromHandler( 'healthy', display_message = False ) + BaseRequest().GetDataFromHandler( 'healthy', display_message = False ) diff --git a/python/ycm/omni_completer.py b/python/ycm/omni_completer.py index fcdb01f7..d40da07b 100644 --- a/python/ycm/omni_completer.py +++ b/python/ycm/omni_completer.py @@ -128,6 +128,6 @@ class OmniCompleter( Completer ): 'query': query } - response = BaseRequest.PostDataToHandler( request_data, - 'filter_and_sort_candidates' ) + response = BaseRequest().PostDataToHandler( request_data, + 'filter_and_sort_candidates' ) return response if response is not None else [] diff --git a/python/ycm/tests/__init__.py b/python/ycm/tests/__init__.py index 7bfe8842..da526310 100644 --- a/python/ycm/tests/__init__.py +++ b/python/ycm/tests/__init__.py @@ -64,7 +64,7 @@ def MakeUserOptions( custom_options = {} ): def _IsReady(): - return BaseRequest.GetDataFromHandler( 'ready' ) + return BaseRequest().GetDataFromHandler( 'ready' ) def WaitUntilReady( timeout = 5 ): diff --git a/python/ycm/tests/event_notification_test.py b/python/ycm/tests/event_notification_test.py index 096b547f..f4cc6e6a 100644 --- a/python/ycm/tests/event_notification_test.py +++ b/python/ycm/tests/event_notification_test.py @@ -125,6 +125,8 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test( call( ERROR_TEXT, truncate = True ) ] ) + ok_( not ycm.ShouldResendFileParseRequest() ) + # But it does if a subsequent event raises again ycm.OnFileReadyToParse() ok_( ycm.FileParseRequestReady() ) @@ -134,6 +136,8 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_test( call( ERROR_TEXT, truncate = True ) ] ) + ok_( not ycm.ShouldResendFileParseRequest() ) + @YouCompleteMeInstance() def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test( @@ -154,20 +158,16 @@ def EventNotification_FileReadyToParse_NonDiagnostic_Error_NonNative_test( test_utils.VIM_SIGNS, contains() ) + ok_( not ycm.ShouldResendFileParseRequest() ) -@patch( 'ycm.client.base_request._LoadExtraConfFile', - new_callable = ExtendedMock ) -@patch( 'ycm.client.base_request._IgnoreExtraConfFile', - new_callable = ExtendedMock ) @YouCompleteMeInstance() def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test( - ycm, ignore_extra_conf, load_extra_conf ): + ycm ): # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest # in combination with YouCompleteMe.OnFileReadyToParse when the completer # raises the (special) UnknownExtraConf exception - FILE_NAME = 'a_file' MESSAGE = ( 'Found ' + FILE_NAME + '. Load? \n\n(Question can be ' 'turned off with options, see YCM docs)' ) @@ -175,86 +175,98 @@ def EventNotification_FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test( def UnknownExtraConfResponse( *args ): raise UnknownExtraConf( FILE_NAME ) - with MockArbitraryBuffer( 'javascript' ): - with MockEventNotification( UnknownExtraConfResponse ): + with patch( 'ycm.client.base_request.BaseRequest.PostDataToHandler', + new_callable = ExtendedMock ) as post_data_to_handler: + with MockArbitraryBuffer( 'javascript' ): + with MockEventNotification( UnknownExtraConfResponse ): - # When the user accepts the extra conf, we load it - with patch( 'ycm.vimsupport.PresentDialog', - return_value = 0, - new_callable = ExtendedMock ) as present_dialog: - ycm.OnFileReadyToParse() - ok_( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() + # When the user accepts the extra conf, we load it + with patch( 'ycm.vimsupport.PresentDialog', + return_value = 0, + new_callable = ExtendedMock ) as present_dialog: + ycm.OnFileReadyToParse() + ok_( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - load_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - ] ) + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) - # Subsequent calls don't re-raise the warning - ycm.HandleFileParseRequest() + # Subsequent calls don't re-raise the warning + ycm.HandleFileParseRequest() - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ) - ] ) - load_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - ] ) + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ) + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) - # But it does if a subsequent event raises again - ycm.OnFileReadyToParse() - ok_( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() + ok_( ycm.ShouldResendFileParseRequest() ) - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - load_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - call( FILE_NAME ), - ] ) + # But it does if a subsequent event raises again + ycm.OnFileReadyToParse() + ok_( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() - # When the user rejects the extra conf, we reject it - with patch( 'ycm.vimsupport.PresentDialog', - return_value = 1, - new_callable = ExtendedMock ) as present_dialog: - ycm.OnFileReadyToParse() - ok_( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ), + call( { 'filepath': FILE_NAME }, 'load_extra_conf_file' ) + ] ) - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - ignore_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - ] ) + ok_( ycm.ShouldResendFileParseRequest() ) - # Subsequent calls don't re-raise the warning - ycm.HandleFileParseRequest() + post_data_to_handler.reset_mock() - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ) - ] ) - ignore_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - ] ) + # When the user rejects the extra conf, we reject it + with patch( 'ycm.vimsupport.PresentDialog', + return_value = 1, + new_callable = ExtendedMock ) as present_dialog: + ycm.OnFileReadyToParse() + ok_( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() - # But it does if a subsequent event raises again - ycm.OnFileReadyToParse() - ok_( ycm.FileParseRequestReady() ) - ycm.HandleFileParseRequest() + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) - present_dialog.assert_has_exact_calls( [ - PresentDialog_Confirm_Call( MESSAGE ), - PresentDialog_Confirm_Call( MESSAGE ), - ] ) - ignore_extra_conf.assert_has_exact_calls( [ - call( FILE_NAME ), - call( FILE_NAME ), - ] ) + # Subsequent calls don't re-raise the warning + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ) + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) + + ok_( ycm.ShouldResendFileParseRequest() ) + + # But it does if a subsequent event raises again + ycm.OnFileReadyToParse() + ok_( ycm.FileParseRequestReady() ) + ycm.HandleFileParseRequest() + + present_dialog.assert_has_exact_calls( [ + PresentDialog_Confirm_Call( MESSAGE ), + PresentDialog_Confirm_Call( MESSAGE ), + ] ) + post_data_to_handler.assert_has_exact_calls( [ + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ), + call( { 'filepath': FILE_NAME }, 'ignore_extra_conf_file' ) + ] ) + + ok_( ycm.ShouldResendFileParseRequest() ) @YouCompleteMeInstance() @@ -302,6 +314,8 @@ def _Check_FileReadyToParse_Diagnostic_Error( ycm ): eq_( ycm.GetErrorCount(), 1 ) eq_( ycm.GetWarningCount(), 0 ) + ok_( not ycm.ShouldResendFileParseRequest() ) + # New identical requests should result in the same diagnostics. ycm.OnFileReadyToParse() ok_( ycm.FileParseRequestReady() ) @@ -315,6 +329,8 @@ def _Check_FileReadyToParse_Diagnostic_Error( ycm ): eq_( ycm.GetErrorCount(), 1 ) eq_( ycm.GetWarningCount(), 0 ) + ok_( not ycm.ShouldResendFileParseRequest() ) + def _Check_FileReadyToParse_Diagnostic_Warning( ycm ): # Tests Vim sign placement/unplacement and error/warning count python API @@ -353,6 +369,8 @@ def _Check_FileReadyToParse_Diagnostic_Warning( ycm ): eq_( ycm.GetErrorCount(), 0 ) eq_( ycm.GetWarningCount(), 1 ) + ok_( not ycm.ShouldResendFileParseRequest() ) + def _Check_FileReadyToParse_Diagnostic_Clean( ycm ): # Tests Vim sign unplacement and error/warning count python API @@ -368,6 +386,7 @@ def _Check_FileReadyToParse_Diagnostic_Clean( ycm ): ) eq_( ycm.GetErrorCount(), 0 ) eq_( ycm.GetWarningCount(), 0 ) + ok_( not ycm.ShouldResendFileParseRequest() ) @patch( 'ycm.youcompleteme.YouCompleteMe._AddUltiSnipsDataIfNeeded' ) diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 85c7373e..da5e356e 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -228,7 +228,7 @@ class YouCompleteMe( object ): def CheckIfServerIsReady( self ): if not self._server_is_ready_with_cache and self.IsServerAlive(): - self._server_is_ready_with_cache = BaseRequest.GetDataFromHandler( + self._server_is_ready_with_cache = BaseRequest().GetDataFromHandler( 'ready', display_message = False ) return self._server_is_ready_with_cache @@ -329,8 +329,8 @@ class YouCompleteMe( object ): def GetDefinedSubcommands( self ): - subcommands = BaseRequest.PostDataToHandler( BuildRequestData(), - 'defined_subcommands' ) + subcommands = BaseRequest().PostDataToHandler( BuildRequestData(), + 'defined_subcommands' ) return subcommands if subcommands else [] @@ -555,6 +555,10 @@ class YouCompleteMe( object ): current_buffer.MarkResponseHandled() + def ShouldResendFileParseRequest( self ): + return self.CurrentBuffer().ShouldResendParseRequest() + + def DebugInfo( self ): debug_info = '' if self._client_logfile: @@ -644,7 +648,7 @@ class YouCompleteMe( object ): def ShowDetailedDiagnostic( self ): - detailed_diagnostic = BaseRequest.PostDataToHandler( + detailed_diagnostic = BaseRequest().PostDataToHandler( BuildRequestData(), 'detailed_diagnostic' ) if 'message' in detailed_diagnostic: