mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-12 10:14:08 +00:00
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
"""Discovers explicitly installed SolidLSP language-server adapters."""
|
|
|
|
import logging
|
|
import threading
|
|
from dataclasses import dataclass
|
|
from importlib import metadata
|
|
|
|
from solidlsp.ls_config import _restore_registered_language_servers, _snapshot_registered_language_servers
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
ENTRY_POINT_GROUP = "serena.language_servers"
|
|
|
|
|
|
@dataclass
|
|
class _DiscoveryState:
|
|
completed: bool = False
|
|
|
|
|
|
_discovery_state = _DiscoveryState()
|
|
_discovery_lock = threading.Lock()
|
|
|
|
|
|
def discover_registered_language_server_adapters() -> None:
|
|
"""Load installed adapters before project configuration resolves language-server IDs.
|
|
|
|
First discovery is serialized because registration and rollback mutate one
|
|
process-global registry. Metadata-enumeration failures leave discovery
|
|
incomplete so a later project load can retry. After successful enumeration,
|
|
each entry point is applied transactionally; a failing adapter is rolled back
|
|
without preventing the remaining installed adapters from registering.
|
|
"""
|
|
with _discovery_lock:
|
|
if _discovery_state.completed:
|
|
return
|
|
|
|
try:
|
|
entry_points = metadata.entry_points(group=ENTRY_POINT_GROUP)
|
|
except Exception as error:
|
|
log.exception("Failed to discover language server adapter entry points: %s", error)
|
|
return
|
|
|
|
for entry_point in entry_points:
|
|
registry_snapshot = _snapshot_registered_language_servers()
|
|
try:
|
|
registration = entry_point.load()
|
|
if not callable(registration):
|
|
raise TypeError("entry point must resolve to a callable registration function")
|
|
registration()
|
|
except Exception as error:
|
|
_restore_registered_language_servers(registry_snapshot)
|
|
log.exception(
|
|
"Failed to load language server adapter entry point '%s' from %s: %s",
|
|
entry_point.name,
|
|
_get_distribution_name(entry_point),
|
|
error,
|
|
)
|
|
|
|
_discovery_state.completed = True
|
|
|
|
|
|
def _get_distribution_name(entry_point: metadata.EntryPoint) -> str:
|
|
distribution = getattr(entry_point, "dist", None)
|
|
if distribution is None:
|
|
return "unknown distribution"
|
|
return distribution.name or "unknown distribution"
|
|
|
|
|
|
def _reset_language_server_adapter_discovery_for_tests() -> None:
|
|
"""Reset discovery state for deterministic in-process tests."""
|
|
with _discovery_lock:
|
|
_discovery_state.completed = False
|