diff --git a/ycmd/completers/language_server/language_server_completer.py b/ycmd/completers/language_server/language_server_completer.py index a2796e81ce..b5fd6ee182 100644 --- a/ycmd/completers/language_server/language_server_completer.py +++ b/ycmd/completers/language_server/language_server_completer.py @@ -1005,7 +1005,8 @@ def __init__( self, user_options, connection_type = 'stdio' ): self._on_file_ready_to_parse_handlers = [] self.RegisterOnFileReadyToParse( lambda self, request_data: - self._UpdateServerWithFileContents( request_data ) + self._UpdateServerWithFileContents( request_data ), + True # once ) self._signature_help_disabled = user_options[ 'disable_signature_help' ] @@ -1898,6 +1899,12 @@ def OnFileReadyToParse( self, request_data ): if not self.ServerIsHealthy(): return + def ClearOneshotHandlers(): + self._on_file_ready_to_parse_handlers = [ + ( handler, once ) for handler, once + in self._on_file_ready_to_parse_handlers if not once + ] + # If we haven't finished initializing yet, we need to queue up all functions # registered on the FileReadyToParse event and in particular # _UpdateServerWithFileContents in reverse order of registration. This @@ -1905,13 +1912,17 @@ def OnFileReadyToParse( self, request_data ): # messages. This is important because server start up can be quite slow and # we must not block the user, while we must keep the server synchronized. if not self._initialize_event.is_set(): - for handler in reversed( self._on_file_ready_to_parse_handlers ): + for handler, _ in reversed( self._on_file_ready_to_parse_handlers ): self._OnInitializeComplete( partial( handler, request_data = request_data ) ) + ClearOneshotHandlers() return - for handler in reversed( self._on_file_ready_to_parse_handlers ): + for handler, _ in reversed( self._on_file_ready_to_parse_handlers ): handler( self, request_data ) + ClearOneshotHandlers() + + self._UpdateServerWithFileContents( request_data ) # Return the latest diagnostics that we have received. # @@ -2480,8 +2491,8 @@ def _OnInitializeComplete( self, handler ): self._on_initialize_complete_handlers.append( handler ) - def RegisterOnFileReadyToParse( self, handler ): - self._on_file_ready_to_parse_handlers.append( handler ) + def RegisterOnFileReadyToParse( self, handler, once=False ): + self._on_file_ready_to_parse_handlers.append( ( handler, once ) ) def GetHoverResponse( self, request_data ): diff --git a/ycmd/tests/rust/__init__.py b/ycmd/tests/rust/__init__.py index 4c604f7837..16df6fed84 100644 --- a/ycmd/tests/rust/__init__.py +++ b/ycmd/tests/rust/__init__.py @@ -44,10 +44,11 @@ def tearDownModule(): def StartRustCompleterServerInDirectory( app, directory ): app.post_json( '/event_notification', - BuildRequest( - filepath = os.path.join( directory, 'src', 'main.rs' ), - event_name = 'FileReadyToParse', - filetype = 'rust' ) ) + BuildRequest( filepath = os.path.join( directory, + 'src', + 'main.rs' ), + event_name = 'FileReadyToParse', + filetype = 'rust' ) ) WaitUntilCompleterServerReady( app, 'rust' ) diff --git a/ycmd/tests/test_utils.py b/ycmd/tests/test_utils.py index e7b725db4a..55b091b5d2 100644 --- a/ycmd/tests/test_utils.py +++ b/ycmd/tests/test_utils.py @@ -16,6 +16,7 @@ # along with ycmd. If not, see . +from ycmd.utils import ReadFile from hamcrest import ( assert_that, contains_exactly, contains_string, @@ -76,9 +77,22 @@ } ) +def GetTestFileContents( filename ): + try: + return ReadFile( filename ) + except IOError: + return '' + + def BuildRequest( **kwargs ): filepath = kwargs[ 'filepath' ] if 'filepath' in kwargs else '/foo' - contents = kwargs[ 'contents' ] if 'contents' in kwargs else '' + contents = ( + kwargs[ 'contents' ] + if 'contents' in kwargs + else GetTestFileContents( filepath ) + if 'filepath' in kwargs + else '' + ) filetype = kwargs[ 'filetype' ] if 'filetype' in kwargs else 'foo' filetypes = kwargs[ 'filetypes' ] if 'filetypes' in kwargs else [ filetype ]