Vue: fix, ensure LS is operational before accepting requests

This commit is contained in:
Michael Panchenko committed 2026-04-14 11:23:08 +02:00
1 parent 562bfeaa3b
commit 1558a4e3bc
1 file changed
+65 -23
@@ -156,6 +156,48 @@ class VueLanguageServer(SolidLanguageServer):
self._ts_server_started = False
self._vue_files_indexed = False
self._indexed_vue_file_uris: list[str] = []
self._ls_operational_ready_event = threading.Event()
self._ls_operational_lock = threading.Lock()
self._ls_operational_thread: threading.Thread | None = None
def _warm_up_ls_operational_state(self) -> None:
"""Warm up the Vue language server operational state asynchronously."""
# execute the operational warm-up
try:
self._ensure_ls_operational()
except SolidLSPException:
if not self.server_started:
log.debug("Skipping Vue language server operational warm-up because the server is stopping")
return
log.exception("Error while warming up Vue language server operational state")
except Exception:
log.exception("Error while warming up Vue language server operational state")
def _ensure_ls_operational(self) -> None:
# short-circuit completed warm-up
if self._ls_operational_ready_event.is_set():
return
# serialize the warm-up sequence
with self._ls_operational_lock:
# short-circuit repeated callers after waiting for the lock
if self._ls_operational_ready_event.is_set():
return
# validate server availability
if not self.server_started:
raise SolidLSPException("Language Server not started")
# wait for cross-file reference readiness
if not self._has_waited_for_cross_file_references:
sleep(self._get_wait_time_for_cross_file_referencing())
self._has_waited_for_cross_file_references = True
# index Vue files on the companion TypeScript server
self._ensure_vue_files_indexed_on_ts_server()
# publish operational readiness
self._ls_operational_ready_event.set()
@override
def is_ignored_dirname(self, dirname: str) -> bool:
@@ -283,9 +325,7 @@ class VueLanguageServer(SolidLanguageServer):
return result
def request_file_references(self, relative_file_path: str) -> list:
if not self.server_started:
log.error("request_file_references called before Language Server started")
raise SolidLSPException("Language Server not started")
self._ensure_ls_operational()
absolute_file_path = os.path.join(self.repository_root_path, relative_file_path)
uri = PathUtils.path_to_uri(absolute_file_path)
@@ -348,15 +388,7 @@ class VueLanguageServer(SolidLanguageServer):
@override
def request_references(self, relative_file_path: str, line: int, column: int) -> list[ls_types.Location]:
if not self.server_started:
log.error("request_references called before Language Server started")
raise SolidLSPException("Language Server not started")
if not self._has_waited_for_cross_file_references:
sleep(self._get_wait_time_for_cross_file_referencing())
self._has_waited_for_cross_file_references = True
self._ensure_vue_files_indexed_on_ts_server()
self._ensure_ls_operational()
symbol_refs = self._send_ts_references_request(relative_file_path, line=line, column=column)
if relative_file_path.endswith(".vue"):
@@ -381,20 +413,14 @@ class VueLanguageServer(SolidLanguageServer):
@override
def request_definition(self, relative_file_path: str, line: int, column: int) -> list[ls_types.Location]:
if not self.server_started:
log.error("request_definition called before Language Server started")
raise SolidLSPException("Language Server not started")
self._ensure_ls_operational()
assert self._ts_server is not None
with self._ts_server.open_file(relative_file_path):
return self._ts_server.request_definition(relative_file_path, line, column)
@override
def request_rename_symbol_edit(self, relative_file_path: str, line: int, column: int, new_name: str) -> ls_types.WorkspaceEdit | None:
if not self.server_started:
log.error("request_rename_symbol_edit called before Language Server started")
raise SolidLSPException("Language Server not started")
self._ensure_ls_operational()
assert self._ts_server is not None
with self._ts_server.open_file(relative_file_path):
return self._ts_server.request_rename_symbol_edit(relative_file_path, line, column, new_name)
@@ -698,6 +724,15 @@ class VueLanguageServer(SolidLanguageServer):
else:
log.info("Vue server initialization complete")
# kick off asynchronous operational warm-up
self._ls_operational_ready_event.clear()
self._ls_operational_thread = threading.Thread(
target=self._warm_up_ls_operational_state,
name="vue-ls-operational-warmup",
daemon=True,
)
self._ls_operational_thread.start()
def _find_tsconfig_for_file(self, file_path: str) -> str | None:
if not file_path:
tsconfig_path = os.path.join(self.repository_root_path, "tsconfig.json")
@@ -724,8 +759,14 @@ class VueLanguageServer(SolidLanguageServer):
@override
def stop(self, shutdown_timeout: float = 5.0) -> None:
self._cleanup_indexed_vue_files()
self._stop_typescript_server()
# serialize shutdown with operational warm-up
with self._ls_operational_lock:
self.server_started = False
self._ls_operational_ready_event.clear()
self._cleanup_indexed_vue_files()
self._stop_typescript_server()
self._ls_operational_thread = None
super().stop(shutdown_timeout)
@override
@@ -760,8 +801,9 @@ class VueLanguageServer(SolidLanguageServer):
return self._filter_shorthand_property_duplicates(symbols)
@staticmethod
def _filter_shorthand_property_duplicates(
self, symbols: list[DocumentSymbol] | list[SymbolInformation]
symbols: list[DocumentSymbol] | list[SymbolInformation],
) -> list[DocumentSymbol] | list[SymbolInformation]:
"""
Filter out Property symbols that have a matching Variable symbol with the same name.