Skip to content

Commit

Permalink
Only parse the initial file the first time it's opened
Browse files Browse the repository at this point in the history
When the language_server_completer is first constructed, it's usually a
FileReadyToParse callback. But at that time, we can't actually parse
anything because we haven't done the initialization exchange with the
server, so we queue this up for later.

The way this was implemenented was to register a "on file ready to
parse" callback to update the server with the file contents at the time
the completer was constructed. In practice, as the completer is usually
constructed via a file ready to pase event, this actually happens
immediately and we then shunt the request into "OnInitializeComplete"
handlers, which are fired when the initialize exchange happens.

Why don't we just put something directly in OnInitializeComplete
handlers? Well because in the constructor, we don't have the
request_data - we only get that in the OnFileReadyToParse callback, so
we have this jank. WCGW?

As a result of this dance, we actually introduced a subtle error.
OnFileReadyToParse handlers are called _every time_ we have a file parse
event, and - we never _remove_ this "parse the file contents as they
were at the time of initialization" request from the "file ready to
parse handlers" list. So every time we has a file parse event, we update
the server with _stale_ data, then immediately correct it! In practice,
this isn't all that noticable, but it is braindead.

Simple solution is to add a "once" flag to the handler creating oneshot
handlers, and clear such handlers after firing them (or converting them
to initialize handlers). Jank to fix jank. It might be better to find a
way to just remove these handler "lists" altogether, as there is
only one other place in the codebase where one is registered, and we
could always just pass request_data to constructor if we have it...
  • Loading branch information
puremourning committed Dec 15, 2023
1 parent 0a276f7 commit 53ceb94
Showing 1 changed file with 14 additions and 5 deletions.
19 changes: 14 additions & 5 deletions ycmd/completers/language_server/language_server_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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' ]
Expand Down Expand Up @@ -1898,20 +1899,28 @@ 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
# ensures that the server is up to date as soon as we are able to send more
# 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()

# Return the latest diagnostics that we have received.
#
Expand Down Expand Up @@ -2480,8 +2489,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 ):
Expand Down

0 comments on commit 53ceb94

Please sign in to comment.