diff --git a/packages/core/src/robotcode/core/async_itertools.py b/packages/core/src/robotcode/core/async_itertools.py deleted file mode 100644 index b30ced6ca..000000000 --- a/packages/core/src/robotcode/core/async_itertools.py +++ /dev/null @@ -1,93 +0,0 @@ -import inspect -from typing import ( - AsyncIterable, - AsyncIterator, - Awaitable, - Callable, - Iterable, - Optional, - TypeVar, - Union, - cast, -) - -__all__ = ["async_chain", "async_chain_iterator", "async_takewhile", "async_dropwhile"] - -_T = TypeVar("_T") -AnyIterable = Union[Iterable[_T], AsyncIterable[_T]] - - -async def async_chain(*iterables: AnyIterable[_T]) -> AsyncIterator[_T]: - for iterable in iterables: - if isinstance(iterable, AsyncIterable): - async for v in iterable: - yield v - else: - for v in iterable: - yield v - - -async def async_chain_iterator(iterator: AsyncIterator[AnyIterable[_T]]) -> AsyncIterator[_T]: - async for e in iterator: - async for v in async_chain(e): - yield v - - -async def __call_predicate(predicate: Union[Callable[[_T], bool], Callable[[_T], Awaitable[bool]]], e: _T) -> bool: - result = predicate(e) - if inspect.isawaitable(result): - return await cast(Awaitable[bool], result) - return cast(bool, result) - - -async def iter_any_iterable(iterable: AnyIterable[_T]) -> AsyncIterator[_T]: - if isinstance(iterable, AsyncIterable): - async for e in iterable: - yield e - else: - for e in iterable: - yield e - - -async def async_takewhile( - predicate: Union[Callable[[_T], bool], Callable[[_T], Awaitable[bool]]], - iterable: AnyIterable[_T], -) -> AsyncIterator[_T]: - async for e in iter_any_iterable(iterable): - result = await __call_predicate(predicate, e) - if result: - yield e - else: - break - - -async def async_dropwhile( - predicate: Union[Callable[[_T], bool], Callable[[_T], Awaitable[bool]]], - iterable: AnyIterable[_T], -) -> AsyncIterator[_T]: - result: Union[bool, Awaitable[bool]] = True - - async for e in iter_any_iterable(iterable): - if not result: - yield e - else: - result = await __call_predicate(predicate, e) - - if not result: - yield e - - -class __NotSet: # noqa: N801 - pass - - -__NOT_SET = __NotSet() - - -async def async_next(__i: AsyncIterator[_T], __default: Union[_T, None, __NotSet] = __NOT_SET) -> Optional[_T]: - try: - return await __i.__anext__() - except StopAsyncIteration: - if __default is __NOT_SET: - raise - return cast(_T, __default) diff --git a/packages/core/src/robotcode/core/async_tools.py b/packages/core/src/robotcode/core/async_tools.py index a8be5c7ee..450a66e64 100644 --- a/packages/core/src/robotcode/core/async_tools.py +++ b/packages/core/src/robotcode/core/async_tools.py @@ -519,29 +519,6 @@ def set_result(w: asyncio.Future[Any], ev: threading.Event) -> None: self._wake_up_first() -class RLock(Lock): - def __init__(self) -> None: - super().__init__() - self._task: Optional[asyncio.Task[Any]] = None - self._depth = 0 - - async def acquire(self) -> bool: - if self._task is None or self._task != asyncio.current_task(): - await super().acquire() - self._task = asyncio.current_task() - assert self._depth == 0 - self._depth += 1 - - return True - - def release(self) -> None: - if self._depth > 0: - self._depth -= 1 - if self._depth == 0: - super().release() - self._task = None - - _global_futures_set: Set[asyncio.Future[Any]] = set() diff --git a/packages/core/src/robotcode/core/concurrent.py b/packages/core/src/robotcode/core/concurrent.py index 4918bcc87..6b9105018 100644 --- a/packages/core/src/robotcode/core/concurrent.py +++ b/packages/core/src/robotcode/core/concurrent.py @@ -3,6 +3,8 @@ from threading import Event, RLock, Thread, current_thread, local from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, TypeVar, cast, overload +from typing_extensions import ParamSpec + _F = TypeVar("_F", bound=Callable[..., Any]) _TResult = TypeVar("_TResult") @@ -118,7 +120,10 @@ def _remove_future_from_running_callables(future: FutureEx[Any]) -> None: _running_callables.pop(future, None) -def run_in_thread(callable: Callable[..., _TResult], *args: Any, **kwargs: Any) -> FutureEx[_TResult]: +_P = ParamSpec("_P") + + +def run_in_thread(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P.kwargs) -> FutureEx[_TResult]: future: FutureEx[_TResult] = FutureEx() with _running_callables_lock: thread = Thread( diff --git a/packages/debugger/src/robotcode/debugger/launcher/client.py b/packages/debugger/src/robotcode/debugger/launcher/client.py index 3a0bf4c09..46167a1c3 100644 --- a/packages/debugger/src/robotcode/debugger/launcher/client.py +++ b/packages/debugger/src/robotcode/debugger/launcher/client.py @@ -64,7 +64,7 @@ def __del__(self) -> None: self.close() @_logger.call - async def on_connection_lost(self, sender: Any, exc: Optional[BaseException]) -> None: + def on_connection_lost(self, sender: Any, exc: Optional[BaseException]) -> None: if sender == self._protocol: self._protocol = None diff --git a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py index 03dd46b42..b21f94796 100644 --- a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py +++ b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py @@ -33,11 +33,11 @@ ) from robotcode.core.async_tools import ( - async_event, create_sub_task, run_coroutine_in_thread, ) from robotcode.core.concurrent import FutureEx, is_threaded_callable, run_in_thread +from robotcode.core.event import event from robotcode.core.utils.dataclasses import as_json, from_dict from robotcode.core.utils.inspect import ensure_coroutine, iter_methods from robotcode.core.utils.logging import LoggingDescriptor @@ -374,12 +374,12 @@ def __init__(self) -> None: def loop(self) -> Optional[asyncio.AbstractEventLoop]: return self._loop - @async_event - async def on_connection_made(sender, transport: asyncio.BaseTransport) -> None: + @event + def on_connection_made(sender, transport: asyncio.BaseTransport) -> None: ... - @async_event - async def on_connection_lost(sender, exc: Optional[BaseException]) -> None: + @event + def on_connection_lost(sender, exc: Optional[BaseException]) -> None: ... def connection_made(self, transport: asyncio.BaseTransport) -> None: @@ -390,10 +390,10 @@ def connection_made(self, transport: asyncio.BaseTransport) -> None: if isinstance(transport, asyncio.WriteTransport): self.write_transport = transport - create_sub_task(self.on_connection_made(self, transport)) + self.on_connection_made(self, transport) def connection_lost(self, exc: Optional[BaseException]) -> None: - create_sub_task(self.on_connection_lost(self, exc)) + self.on_connection_lost(self, exc) self._loop = None def eof_received(self) -> Optional[bool]: @@ -451,6 +451,7 @@ def __init__(self) -> None: self._received_request: OrderedDict[Union[str, int, None], ReceivedRequestEntry] = OrderedDict() self._received_request_lock = threading.RLock() self._signature_cache: Dict[Callable[..., Any], inspect.Signature] = {} + self._running_handle_message_tasks: Set[asyncio.Future[Any]] = set() @staticmethod def _generate_json_rpc_messages_from_dict( @@ -494,15 +495,10 @@ def _handle_body(self, body: bytes, charset: str) -> None: self.send_error(JsonRPCErrors.PARSE_ERROR, f"{type(e).__name__}: {e}") def _handle_messages(self, iterator: Iterator[JsonRPCMessage]) -> None: - def done(f: asyncio.Future[Any]) -> None: - if f.done() and not f.cancelled(): - ex = f.exception() - - if ex is None or isinstance(ex, asyncio.CancelledError): - return - for m in iterator: - create_sub_task(self.handle_message(m)).add_done_callback(done) + task = asyncio.create_task(self.handle_message(m)) + self._running_handle_message_tasks.add(task) + task.add_done_callback(self._running_handle_message_tasks.discard) @__logger.call async def handle_message(self, message: JsonRPCMessage) -> None: diff --git a/packages/language_server/src/robotcode/language_server/common/parts/code_lens.py b/packages/language_server/src/robotcode/language_server/common/parts/code_lens.py index 1895bf3df..3eafdd09d 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/code_lens.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/code_lens.py @@ -1,7 +1,7 @@ from concurrent.futures import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, threaded +from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, threaded from robotcode.core.event import event from robotcode.core.lsp.types import ( CodeLens, @@ -90,13 +90,20 @@ def _code_lens_resolve(self, params: CodeLens, *args: Any, **kwargs: Any) -> Cod return params - def refresh(self) -> None: - if not ( + def refresh(self, now: bool = True) -> None: + if self.refresh_task is not None and not self.refresh_task.done(): + self.refresh_task.cancel() + + self.refresh_task = run_in_thread(self._refresh, now) + + def _refresh(self, now: bool = True) -> None: + if ( self.parent.client_capabilities is not None and self.parent.client_capabilities.workspace is not None and self.parent.client_capabilities.workspace.code_lens is not None and self.parent.client_capabilities.workspace.code_lens.refresh_support ): - return + if not now: + check_current_thread_canceled(1) - self.parent.send_request("workspace/codeLens/refresh").result(self._refresh_timeout) + self.parent.send_request("workspace/codeLens/refresh").result(self._refresh_timeout) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py b/packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py index eb1dd8b32..5725e852d 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py @@ -100,6 +100,8 @@ def __init__(self, protocol: "LanguageServerProtocol") -> None: self.client_supports_pull = False + self._refresh_timeout = 5 + def server_initialized(self, sender: Any) -> None: self._workspace_diagnostics_task = run_in_thread(self.run_workspace_diagnostics) @@ -221,6 +223,8 @@ def run_workspace_diagnostics(self) -> None: "Analyse workspace", cancellable=False, current=0, max=len(documents) + 1, start=False ) as progress: for i, document in enumerate(documents): + check_current_thread_canceled() + mode = self.get_diagnostics_mode(document.uri) if mode == DiagnosticsMode.OFF: self.get_diagnostics_data(document).version = document.version @@ -418,23 +422,20 @@ def get_diagnostics_mode(self, uri: Uri) -> DiagnosticsMode: return DiagnosticsMode.OPENFILESONLY - def __do_refresh(self, now: bool = False) -> None: - if not now: - check_current_thread_canceled(1) - - self.__refresh() - def refresh(self, now: bool = False) -> None: if self.refresh_task is not None and not self.refresh_task.done(): self.refresh_task.cancel() - self.refresh_task = run_in_thread(self.__do_refresh, now) + self.refresh_task = run_in_thread(self._refresh, now) - def __refresh(self) -> None: + def _refresh(self, now: bool = False) -> None: if ( self.parent.client_capabilities and self.parent.client_capabilities.workspace and self.parent.client_capabilities.workspace.diagnostics and self.parent.client_capabilities.workspace.diagnostics.refresh_support ): - self.parent.send_request("workspace/diagnostic/refresh").result(30) + if not now: + check_current_thread_canceled(1) + + self.parent.send_request("workspace/diagnostic/refresh").result(self._refresh_timeout) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/inlay_hint.py b/packages/language_server/src/robotcode/language_server/common/parts/inlay_hint.py index e7ffc85ec..d2f3fe87b 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/inlay_hint.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/inlay_hint.py @@ -1,7 +1,7 @@ from concurrent.futures import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import threaded +from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, threaded from robotcode.core.event import event from robotcode.core.lsp.types import ( InlayHint, @@ -27,6 +27,8 @@ class InlayHintProtocolPart(LanguageServerProtocolPart): def __init__(self, parent: "LanguageServerProtocol") -> None: super().__init__(parent) + self.refresh_task: Optional[FutureEx[Any]] = None + self._refresh_timeout = 5 @event def collect(sender, document: TextDocument, range: Range) -> Optional[List[InlayHint]]: # NOSONAR @@ -98,11 +100,20 @@ def _inlay_hint_resolve( return params - def refresh(self) -> None: + def refresh(self, now: bool = True) -> None: + if self.refresh_task is not None and not self.refresh_task.done(): + self.refresh_task.cancel() + + self.refresh_task = run_in_thread(self._refresh, now) + + def _refresh(self, now: bool = True) -> None: if ( self.parent.client_capabilities is not None and self.parent.client_capabilities.workspace is not None and self.parent.client_capabilities.workspace.inlay_hint is not None and self.parent.client_capabilities.workspace.inlay_hint.refresh_support ): - self.parent.send_request("workspace/inlayHint/refresh").result(30) + if not now: + check_current_thread_canceled(1) + + self.parent.send_request("workspace/inlayHint/refresh").result(self._refresh_timeout) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/inline_value.py b/packages/language_server/src/robotcode/language_server/common/parts/inline_value.py index 35375a736..d7579acf7 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/inline_value.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/inline_value.py @@ -1,7 +1,7 @@ from asyncio import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import check_current_thread_canceled, threaded +from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, threaded from robotcode.core.event import event from robotcode.core.lsp.types import ( DocumentSelector, @@ -31,6 +31,8 @@ class InlineValueProtocolPart(LanguageServerProtocolPart): def __init__(self, parent: "LanguageServerProtocol") -> None: super().__init__(parent) + self.refresh_task: Optional[FutureEx[Any]] = None + self._refresh_timeout = 5 @event def collect( @@ -88,11 +90,20 @@ def _text_document_inline_value( return results - def refresh(self) -> None: + def refresh(self, now: bool = True) -> None: + if self.refresh_task is not None and not self.refresh_task.done(): + self.refresh_task.cancel() + + self.refresh_task = run_in_thread(self._refresh, now) + + def _refresh(self, now: bool = True) -> None: if ( self.parent.client_capabilities and self.parent.client_capabilities.workspace and self.parent.client_capabilities.workspace.inline_value and self.parent.client_capabilities.workspace.inline_value.refresh_support ): - self.parent.send_request("workspace/inlineValue/refresh").result(30) + if not now: + check_current_thread_canceled(1) + + self.parent.send_request("workspace/inlineValue/refresh").result(self._refresh_timeout) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/semantic_tokens.py b/packages/language_server/src/robotcode/language_server/common/parts/semantic_tokens.py index 3c4d9e755..f6413a2a2 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/semantic_tokens.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/semantic_tokens.py @@ -1,8 +1,8 @@ from concurrent.futures import CancelledError from enum import Enum -from typing import TYPE_CHECKING, Any, Final, List, Union +from typing import TYPE_CHECKING, Any, Final, List, Optional, Union -from robotcode.core.concurrent import check_current_thread_canceled, threaded +from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, threaded from robotcode.core.event import event from robotcode.core.lsp.types import ( Range, @@ -37,6 +37,9 @@ class SemanticTokensProtocolPart(LanguageServerProtocolPart): def __init__(self, parent: "LanguageServerProtocol") -> None: super().__init__(parent) + self.refresh_task: Optional[FutureEx[Any]] = None + self._refresh_timeout = 5 + self.token_types: List[Enum] = list(SemanticTokenTypes) self.token_modifiers: List[Enum] = list(SemanticTokenModifiers) @@ -168,11 +171,20 @@ def _text_document_semantic_tokens_range( return None - def refresh(self) -> None: + def refresh(self, now: bool = True) -> None: + if self.refresh_task is not None and not self.refresh_task.done(): + self.refresh_task.cancel() + + self.refresh_task = run_in_thread(self._refresh, now) + + def _refresh(self, now: bool = True) -> None: if ( self.parent.client_capabilities is not None and self.parent.client_capabilities.workspace is not None and self.parent.client_capabilities.workspace.semantic_tokens is not None and self.parent.client_capabilities.workspace.semantic_tokens.refresh_support ): - self.parent.send_request("workspace/semanticTokens/refresh").result(30) + if not now: + check_current_thread_canceled(1) + + self.parent.send_request("workspace/semanticTokens/refresh").result(self._refresh_timeout) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/window.py b/packages/language_server/src/robotcode/language_server/common/parts/window.py index f281890bd..358860651 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/window.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/window.py @@ -192,7 +192,7 @@ def create_progress(self) -> Optional[ProgressToken]: and self.parent.client_capabilities.window.work_done_progress ): token = str(uuid.uuid4()) - self.parent.send_request("window/workDoneProgress/create", WorkDoneProgressCreateParams(token)).result(30) + self.parent.send_request("window/workDoneProgress/create", WorkDoneProgressCreateParams(token)) self.__progress_tokens[token] = False return token diff --git a/packages/language_server/src/robotcode/language_server/common/parts/workspace.py b/packages/language_server/src/robotcode/language_server/common/parts/workspace.py index 1f142dbf8..ce05df1a9 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/workspace.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/workspace.py @@ -173,6 +173,15 @@ def __init__( self._file_watchers_lock = threading.RLock() self.parent.on_shutdown.add(self.server_shutdown) + self.parent.on_initialize.add(self.server_initialize) + + def server_initialize(self, sender: Any, initialization_options: Optional[Any] = None) -> None: + if ( + initialization_options is not None + and isinstance(initialization_options, dict) + and "settings" in initialization_options + ): + self.settings = initialization_options["settings"] @property def workspace_folders(self) -> List[WorkspaceFolder]: diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/imports_manager.py b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/imports_manager.py index 87646fdda..9e342137b 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/imports_manager.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/imports_manager.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import ast import itertools import multiprocessing as mp @@ -26,6 +24,7 @@ final, ) +from robotcode.core.concurrent import run_in_thread from robotcode.core.event import event from robotcode.core.lsp.types import DocumentUri, FileChangeType, FileEvent from robotcode.core.uri import Uri @@ -100,7 +99,7 @@ def __hash__(self) -> int: class _ImportEntry(ABC): def __init__( self, - parent: ImportsManager, + parent: "ImportsManager", ) -> None: self.parent = parent self.references: weakref.WeakSet[Any] = weakref.WeakSet() @@ -150,7 +149,7 @@ def is_valid(self) -> bool: class _LibrariesEntry(_ImportEntry): def __init__( self, - parent: ImportsManager, + parent: "ImportsManager", name: str, args: Tuple[Any, ...], working_dir: str, @@ -292,7 +291,7 @@ class _ResourcesEntry(_ImportEntry): def __init__( self, name: str, - parent: ImportsManager, + parent: "ImportsManager", get_document_coroutine: Callable[[], TextDocument], ) -> None: super().__init__(parent) @@ -363,11 +362,11 @@ def _get_document(self) -> TextDocument: return self._document - def get_namespace(self) -> Namespace: + def get_namespace(self) -> "Namespace": with self._lock: return self._get_namespace() - def _get_namespace(self) -> Namespace: + def _get_namespace(self) -> "Namespace": return self.parent.parent_protocol.documents_cache.get_resource_namespace(self._get_document()) def get_libdoc(self) -> LibraryDoc: @@ -394,7 +393,7 @@ def __init__( args: Tuple[Any, ...], working_dir: str, base_dir: str, - parent: ImportsManager, + parent: "ImportsManager", get_variables_doc_coroutine: Callable[[str, Tuple[Any, ...], str, str], VariablesDoc], ) -> None: super().__init__(parent) @@ -490,7 +489,7 @@ def filepath_base(self) -> str: class ImportsManager: _logger = LoggingDescriptor() - def __init__(self, parent_protocol: RobotLanguageServerProtocol, folder: Uri, config: RobotCodeConfig) -> None: + def __init__(self, parent_protocol: "RobotLanguageServerProtocol", folder: Uri, config: RobotCodeConfig) -> None: super().__init__() self.parent_protocol = parent_protocol @@ -661,7 +660,7 @@ def _do_imports_changed(self, sender: Any, uri: DocumentUri) -> None: # NOSONAR @language_id("robotframework") def resource_document_changed(self, sender: Any, document: TextDocument) -> None: - self.__resource_document_changed(document) + run_in_thread(self.__resource_document_changed, document) def __resource_document_changed(self, document: TextDocument) -> None: resource_changed: List[LibraryDoc] = [] diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/namespace.py b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/namespace.py index 5b5eea860..dbcf15dc1 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/namespace.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/namespace.py @@ -4,12 +4,12 @@ import enum import itertools import re -import threading import time import weakref from collections import OrderedDict, defaultdict from itertools import chain from pathlib import Path +from threading import RLock from typing import ( Any, Dict, @@ -512,17 +512,17 @@ def __init__( self._resources_matchers: Optional[Dict[KeywordMatcher, ResourceEntry]] = None self._variables: OrderedDict[str, VariablesEntry] = OrderedDict() self._initialized = False - self._initialize_lock = threading.RLock() + self._initialize_lock = RLock() self._analyzed = False - self._analyze_lock = threading.RLock() + self._analyze_lock = RLock() self._library_doc: Optional[LibraryDoc] = None - self._library_doc_lock = threading.RLock() + self._library_doc_lock = RLock() self._imports: Optional[List[Import]] = None self._import_entries: OrderedDict[Import, LibraryEntry] = OrderedDict() self._own_variables: Optional[List[VariableDefinition]] = None - self._own_variables_lock = threading.RLock() + self._own_variables_lock = RLock() self._global_variables: Optional[List[VariableDefinition]] = None - self._global_variables_lock = threading.RLock() + self._global_variables_lock = RLock() self._diagnostics: List[Diagnostic] = [] self._keyword_references: Dict[KeywordDoc, Set[Location]] = {} @@ -531,9 +531,9 @@ def __init__( self._namespace_references: Dict[LibraryEntry, Set[Location]] = {} self._imported_keywords: Optional[List[KeywordDoc]] = None - self._imported_keywords_lock = threading.RLock() + self._imported_keywords_lock = RLock() self._keywords: Optional[List[KeywordDoc]] = None - self._keywords_lock = threading.RLock() + self._keywords_lock = RLock() # TODO: how to get the search order from model self.search_order: Tuple[str, ...] = () @@ -707,6 +707,8 @@ def get_libraries(self) -> OrderedDict[str, LibraryEntry]: return self._libraries def get_namespaces(self) -> Dict[KeywordMatcher, List[LibraryEntry]]: + self.ensure_initialized() + if self._namespaces is None: self._namespaces = defaultdict(list) diff --git a/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json b/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json index 092c3bde5..f87b171fd 100644 --- a/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json +++ b/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json @@ -16,6 +16,7 @@ // "--debugpy", // "--debugpy-wait-for-client", // "--log", + // "--log-level", "TRACE", ], "robotcode.robocop.enabled": false, // "robotcode.debug.groupOutput": false, diff --git a/vscode-client/languageclientsmanger.ts b/vscode-client/languageclientsmanger.ts index f20bd3e52..bb41e08a5 100644 --- a/vscode-client/languageclientsmanger.ts +++ b/vscode-client/languageclientsmanger.ts @@ -413,14 +413,15 @@ export class LanguageClientsManager { vscode.workspace.workspaceFolders?.length === 1 ? [{ scheme: "file", language: "robotframework" }] : [{ scheme: "file", language: "robotframework", pattern: `${workspaceFolder.uri.fsPath}/**/*` }], - // synchronize: { - // configurationSection: [CONFIG_SECTION], - // }, + synchronize: { + configurationSection: [CONFIG_SECTION], + }, initializationOptions: { storageUri: this.extensionContext?.storageUri?.toString(), globalStorageUri: this.extensionContext?.globalStorageUri?.toString(), pythonPath: config.get("robot.pythonPath", []), env: config.get("robot.env", []), + settings: { robotcode: config }, }, revealOutputChannelOn: RevealOutputChannelOn.Never, // TODO: should we make this configurable? initializationFailedHandler: (error: ResponseError | Error | undefined) => {