diff --git a/packages/core/src/robotcode/core/event.py b/packages/core/src/robotcode/core/event.py index 4fe4fbb9a..43f1f3699 100644 --- a/packages/core/src/robotcode/core/event.py +++ b/packages/core/src/robotcode/core/event.py @@ -13,6 +13,7 @@ Optional, Type, TypeVar, + Union, cast, ) @@ -74,12 +75,15 @@ def _notify( *__args: _TParams.args, callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None, **__kwargs: _TParams.kwargs, - ) -> Iterator[_TResult]: + ) -> Iterator[Union[_TResult, BaseException]]: for method in filter( lambda x: callback_filter(x) if callback_filter is not None else True, set(self), ): - yield method(*__args, **__kwargs) + try: + yield method(*__args, **__kwargs) + except BaseException as e: + yield e class EventIterator(EventResultIteratorBase[_TParams, _TResult]): @@ -88,7 +92,7 @@ def __call__( *__args: _TParams.args, callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None, **__kwargs: _TParams.kwargs, - ) -> Iterator[_TResult]: + ) -> Iterator[Union[_TResult, BaseException]]: return self._notify(*__args, callback_filter=callback_filter, **__kwargs) @@ -98,7 +102,7 @@ def __call__( *__args: _TParams.args, callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None, **__kwargs: _TParams.kwargs, - ) -> List[_TResult]: + ) -> List[Union[_TResult, BaseException]]: return list(self._notify(*__args, callback_filter=callback_filter, **__kwargs)) diff --git a/packages/core/src/robotcode/core/utils/threading.py b/packages/core/src/robotcode/core/utils/threading.py index 0fe16727a..a9d699b94 100644 --- a/packages/core/src/robotcode/core/utils/threading.py +++ b/packages/core/src/robotcode/core/utils/threading.py @@ -1,15 +1,18 @@ +import inspect from typing import Any, Callable, TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) +__THREADED_MARKER = "__threaded__" + def threaded(enabled: bool = True) -> Callable[[_F], _F]: def decorator(func: _F) -> _F: - setattr(func, "__threaded__", enabled) + setattr(func, __THREADED_MARKER, enabled) return func return decorator def is_threaded_callable(func: Callable[..., Any]) -> bool: - return getattr(func, "__threaded__", False) + return getattr(func, __THREADED_MARKER, False) or inspect.ismethod(func) and getattr(func, __THREADED_MARKER, False) diff --git a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py index 7304b012e..57872657c 100644 --- a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py +++ b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py @@ -6,6 +6,7 @@ import json import re import threading +import time import weakref from abc import ABC, abstractmethod from collections import OrderedDict @@ -443,6 +444,7 @@ def __init__(self) -> None: self._sended_request_count = 0 self._received_request: OrderedDict[Union[str, int, None], ReceivedRequestEntry] = OrderedDict() self._received_request_lock = threading.RLock() + self._signature_cache: Dict[Callable[..., Any], inspect.Signature] = {} @staticmethod def _generate_json_rpc_messages_from_dict( @@ -640,9 +642,8 @@ async def handle_error(self, message: JsonRPCError) -> None: if not entry.future.done(): entry.future.set_exception(e) - @staticmethod def _convert_params( - callable: Callable[..., Any], params_type: Optional[Type[Any]], params: Any + self, callable: Callable[..., Any], params_type: Optional[Type[Any]], params: Any ) -> Tuple[List[Any], Dict[str, Any]]: if params is None: return [], {} @@ -655,7 +656,12 @@ def _convert_params( # try to convert the dict to correct type converted_params = from_dict(params, params_type) - signature = inspect.signature(callable) + # get the signature of the callable + if callable in self._signature_cache: + signature = self._signature_cache[callable] + else: + signature = inspect.signature(callable) + self._signature_cache[callable] = signature has_var_kw = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in signature.parameters.values()) @@ -705,62 +711,66 @@ def _convert_params( return args, kw_args async def handle_request(self, message: JsonRPCRequest) -> None: - e = self.registry.get_entry(message.method) + start = time.monotonic_ns() + try: + e = self.registry.get_entry(message.method) - if e is None or not callable(e.method): - self.send_error( - JsonRPCErrors.METHOD_NOT_FOUND, - f"Unknown method: {message.method}", - id=message.id, - ) - return + if e is None or not callable(e.method): + self.send_error( + JsonRPCErrors.METHOD_NOT_FOUND, + f"Unknown method: {message.method}", + id=message.id, + ) + return - params = self._convert_params(e.method, e.param_type, message.params) + params = self._convert_params(e.method, e.param_type, message.params) - if not e.is_coroutine: - self.send_response(message.id, e.method(*params[0], **params[1])) - else: - if is_threaded_callable(e.method) or inspect.ismethod(e.method) and is_threaded_callable(e.method.__func__): - task = run_coroutine_in_thread( - ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1] - ) + if not e.is_coroutine: + self.send_response(message.id, e.method(*params[0], **params[1])) else: - task = create_sub_task( - ensure_coroutine(e.method)(*params[0], **params[1]), - name=message.method, - ) + if is_threaded_callable(e.method): + task = run_coroutine_in_thread( + ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1] + ) + else: + task = create_sub_task( + ensure_coroutine(e.method)(*params[0], **params[1]), + name=message.method, + ) + + with self._received_request_lock: + self._received_request[message.id] = ReceivedRequestEntry(task, message, e.cancelable) + + def done(t: asyncio.Future[Any]) -> None: + try: + if not t.cancelled(): + ex = t.exception() + if ex is not None: + self.__logger.exception(ex, exc_info=ex) + raise JsonRPCErrorException( + JsonRPCErrors.INTERNAL_ERROR, f"{type(ex).__name__}: {ex}" + ) from ex + + self.send_response(message.id, t.result()) + except asyncio.CancelledError: + self.__logger.debug(lambda: f"request message {message!r} canceled") + self.send_error(JsonRPCErrors.REQUEST_CANCELLED, "Request canceled.", id=message.id) + except (SystemExit, KeyboardInterrupt): + raise + except JsonRPCErrorException as e: + self.send_error(e.code, e.message or f"{type(e).__name__}: {e}", id=message.id, data=e.data) + except BaseException as e: + self.__logger.exception(e) + self.send_error(JsonRPCErrors.INTERNAL_ERROR, f"{type(e).__name__}: {e}", id=message.id) + finally: + with self._received_request_lock: + self._received_request.pop(message.id, None) + + task.add_done_callback(done) - with self._received_request_lock: - self._received_request[message.id] = ReceivedRequestEntry(task, message, e.cancelable) - - def done(t: asyncio.Future[Any]) -> None: - try: - if not t.cancelled(): - ex = t.exception() - if ex is not None: - self.__logger.exception(ex, exc_info=ex) - raise JsonRPCErrorException( - JsonRPCErrors.INTERNAL_ERROR, f"{type(ex).__name__}: {ex}" - ) from ex - - self.send_response(message.id, t.result()) - except asyncio.CancelledError: - self.__logger.debug(lambda: f"request message {message!r} canceled") - self.send_error(JsonRPCErrors.REQUEST_CANCELLED, "Request canceled.", id=message.id) - except (SystemExit, KeyboardInterrupt): - raise - except JsonRPCErrorException as e: - self.send_error(e.code, e.message or f"{type(e).__name__}: {e}", id=message.id, data=e.data) - except BaseException as e: - self.__logger.exception(e) - self.send_error(JsonRPCErrors.INTERNAL_ERROR, f"{type(e).__name__}: {e}", id=message.id) - finally: - with self._received_request_lock: - self._received_request.pop(message.id, None) - - task.add_done_callback(done) - - await task + await task + finally: + self.__logger.debug(lambda: f"request message {message!r} done in {time.monotonic_ns() - start}ns") def cancel_request(self, id: Union[int, str, None]) -> None: with self._received_request_lock: @@ -788,11 +798,7 @@ async def handle_notification(self, message: JsonRPCNotification) -> None: if not e.is_coroutine: e.method(*params[0], **params[1]) else: - if ( - is_threaded_callable(e.method) - or inspect.ismethod(e.method) - and is_threaded_callable(e.method.__func__) - ): + if is_threaded_callable(e.method): task = run_coroutine_in_thread( ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1] ) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/documents.py b/packages/language_server/src/robotcode/language_server/common/parts/documents.py index 3fa1359a3..6d36bdd28 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/documents.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/documents.py @@ -114,6 +114,9 @@ def read_document_text(self, uri: Uri, language_id: Union[str, Callable[[Any], b for e in self.on_read_document_text( self, uri, callback_filter=language_id_filter(language_id) if isinstance(language_id, str) else language_id ): + if isinstance(e, BaseException): + raise RuntimeError(f"Can't read document text from {uri}: {e}") from e + if e is not None: return self._normalize_line_endings(e) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/hover.py b/packages/language_server/src/robotcode/language_server/common/parts/hover.py index 7087cc25a..b843706a9 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/hover.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/hover.py @@ -1,9 +1,6 @@ -from __future__ import annotations - -from asyncio import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.async_tools import async_tasking_event +from robotcode.core.event import event from robotcode.core.lsp.types import ( Hover, HoverOptions, @@ -31,11 +28,11 @@ class HoverProtocolPart(LanguageServerProtocolPart, HasExtendCapabilities): _logger: Final = LoggingDescriptor() - def __init__(self, parent: LanguageServerProtocol) -> None: + def __init__(self, parent: "LanguageServerProtocol") -> None: super().__init__(parent) - @async_tasking_event - async def collect(sender, document: TextDocument, position: Position) -> Optional[Hover]: # NOSONAR + @event + def collect(sender, document: TextDocument, position: Position) -> Optional[Hover]: ... def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @@ -44,7 +41,7 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @rpc_method(name="textDocument/hover", param_type=HoverParams) @threaded() - async def _text_document_hover( + def _text_document_hover( self, text_document: TextDocumentIdentifier, position: Position, @@ -57,15 +54,14 @@ async def _text_document_hover( if document is None: return None - for result in await self.collect( + for result in self.collect( self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document), ): if isinstance(result, BaseException): - if not isinstance(result, CancelledError): - self._logger.exception(result, exc_info=result) + self._logger.exception(result, exc_info=result) else: if result is not None: results.append(result) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py index 3c8efd557..379b40289 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py @@ -37,6 +37,17 @@ Range, ) from robotcode.core.uri import Uri +from robotcode.robot.diagnostics.entities import ( + ArgumentDefinition, + CommandLineVariableDefinition, + EnvironmentVariableDefinition, + LibraryEntry, + LocalVariableDefinition, + VariableDefinition, + VariableDefinitionType, + VariableNotFoundDefinition, +) +from robotcode.robot.diagnostics.library_doc import KeywordDoc, is_embedded_keyword from robotcode.robot.utils import get_robot_version from robotcode.robot.utils.ast import ( is_not_variable_token, @@ -48,18 +59,7 @@ ) from robotcode.robot.utils.visitor import Visitor -from .entities import ( - ArgumentDefinition, - CommandLineVariableDefinition, - EnvironmentVariableDefinition, - LibraryEntry, - LocalVariableDefinition, - VariableDefinition, - VariableDefinitionType, - VariableNotFoundDefinition, -) from .errors import DIAGNOSTICS_SOURCE_NAME, Error -from .library_doc import KeywordDoc, is_embedded_keyword from .model_helper import ModelHelperMixin from .namespace import ( KeywordFinder, 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 3c95d428e..87646fdda 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 @@ -38,12 +38,8 @@ from robotcode.language_server.common.parts.workspace import FileWatcherEntry, Workspace from robotcode.language_server.common.text_document import TextDocument from robotcode.language_server.robotframework.configuration import CacheSaveLocation, RobotCodeConfig -from robotcode.robot.utils import get_robot_version, get_robot_version_str -from robotcode.robot.utils.robot_path import find_file_ex - -from ...__version__ import __version__ -from .entities import CommandLineVariableDefinition, VariableDefinition -from .library_doc import ( +from robotcode.robot.diagnostics.entities import CommandLineVariableDefinition, VariableDefinition +from robotcode.robot.diagnostics.library_doc import ( ROBOT_LIBRARY_PACKAGE, CompleteResult, LibraryDoc, @@ -64,6 +60,10 @@ resolve_args, resolve_variable, ) +from robotcode.robot.utils import get_robot_version, get_robot_version_str +from robotcode.robot.utils.robot_path import find_file_ex + +from ...__version__ import __version__ if TYPE_CHECKING: from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/model_helper.py b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/model_helper.py index 91140d02d..14d812e12 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/model_helper.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/model_helper.py @@ -22,6 +22,17 @@ from robot.variables.finders import NOT_FOUND, NumberFinder from robot.variables.search import contains_variable, search_variable from robotcode.core.lsp.types import Position +from robotcode.robot.diagnostics.entities import ( + LibraryEntry, + VariableDefinition, + VariableNotFoundDefinition, +) +from robotcode.robot.diagnostics.library_doc import ( + ArgumentInfo, + KeywordArgumentKind, + KeywordDoc, + LibraryDoc, +) from robotcode.robot.utils import get_robot_version from robotcode.robot.utils.ast import ( iter_over_keyword_names_and_owners, @@ -32,17 +43,6 @@ whitespace_from_begin_of_token, ) -from .entities import ( - LibraryEntry, - VariableDefinition, - VariableNotFoundDefinition, -) -from .library_doc import ( - ArgumentInfo, - KeywordArgumentKind, - KeywordDoc, - LibraryDoc, -) from .namespace import ( DEFAULT_BDD_PREFIXES, Namespace, 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 d5e4b4dc0..5b5eea860 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 @@ -47,20 +47,7 @@ ) from robotcode.core.uri import Uri from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.utils import get_robot_version -from robotcode.robot.utils.ast import ( - range_from_node, - range_from_token, - strip_variable_token, - tokenize_variables, -) -from robotcode.robot.utils.match import eq_namespace -from robotcode.robot.utils.variables import BUILTIN_VARIABLES -from robotcode.robot.utils.visitor import Visitor - -from ...common.text_document import TextDocument -from ..languages import Languages -from .entities import ( +from robotcode.robot.diagnostics.entities import ( ArgumentDefinition, BuiltInVariableDefinition, CommandLineVariableDefinition, @@ -77,9 +64,7 @@ VariablesEntry, VariablesImport, ) -from .errors import DIAGNOSTICS_SOURCE_NAME, Error -from .imports_manager import ImportsManager -from .library_doc import ( +from robotcode.robot.diagnostics.library_doc import ( BUILTIN_LIBRARY_NAME, DEFAULT_LIBRARIES, KeywordDoc, @@ -87,6 +72,21 @@ KeywordMatcher, LibraryDoc, ) +from robotcode.robot.utils import get_robot_version +from robotcode.robot.utils.ast import ( + range_from_node, + range_from_token, + strip_variable_token, + tokenize_variables, +) +from robotcode.robot.utils.match import eq_namespace +from robotcode.robot.utils.variables import BUILTIN_VARIABLES +from robotcode.robot.utils.visitor import Visitor + +from ...common.text_document import TextDocument +from ..languages import Languages +from .errors import DIAGNOSTICS_SOURCE_NAME, Error +from .imports_manager import ImportsManager EXTRACT_COMMENT_PATTERN = re.compile(r".*(?:^ *|\t+| {2,})#(?P.*)$") ROBOTCODE_PATTERN = re.compile(r"(?P\brobotcode\b)\s*:\s*(?P\b\w+\b)") diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_documentation.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_documentation.py index d0e408cf1..fd85984d6 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_documentation.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_documentation.py @@ -25,13 +25,17 @@ from robotcode.core.utils.net import find_free_port from robotcode.core.utils.threading import threaded from robotcode.jsonrpc2.protocol import rpc_method +from robotcode.robot.diagnostics.entities import LibraryEntry +from robotcode.robot.diagnostics.library_doc import ( + get_library_doc, + get_robot_library_html_doc_str, + resolve_robot_variables, +) from robotcode.robot.utils.ast import get_node_at_position, range_from_token from ...common.decorators import code_action_kinds, language_id from ...common.text_document import TextDocument from ..configuration import DocumentationServerConfig -from ..diagnostics.entities import LibraryEntry -from ..diagnostics.library_doc import get_library_doc, get_robot_library_html_doc_str, resolve_robot_variables from ..diagnostics.model_helper import ModelHelperMixin from ..diagnostics.namespace import Namespace from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/codelens.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/codelens.py index d4bee7169..7f5a20750 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/codelens.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/codelens.py @@ -6,13 +6,13 @@ from robotcode.core.async_tools import create_sub_task from robotcode.core.lsp.types import CodeLens, Command from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.library_doc import KeywordDoc from robotcode.robot.utils.ast import range_from_token from robotcode.robot.utils.visitor import Visitor from ...common.decorators import language_id from ...common.text_document import TextDocument from ..configuration import AnalysisConfig -from ..diagnostics.library_doc import KeywordDoc from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py index 27dcc3ed9..dc61fe790 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py @@ -63,6 +63,8 @@ TextEdit, ) from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.entities import VariableDefinitionType +from robotcode.robot.diagnostics.library_doc import CompleteResultKind, KeywordArgumentKind, KeywordDoc, KeywordMatcher from robotcode.robot.utils import get_robot_version from robotcode.robot.utils.ast import ( get_nodes_at_position, @@ -76,8 +78,6 @@ from ...common.decorators import language_id, trigger_characters from ...common.text_document import TextDocument from ..configuration import CompletionConfig -from ..diagnostics.entities import VariableDefinitionType -from ..diagnostics.library_doc import CompleteResultKind, KeywordArgumentKind, KeywordDoc, KeywordMatcher from ..diagnostics.model_helper import ModelHelperMixin from ..diagnostics.namespace import DocumentType, Namespace from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py index 475e676f2..31760c4de 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py @@ -9,6 +9,7 @@ from robotcode.core.lsp.types import Diagnostic, DiagnosticSeverity, DiagnosticTag, Position, Range from robotcode.core.uri import Uri from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.entities import ArgumentDefinition from robotcode.robot.utils.ast import iter_nodes, range_from_node, range_from_token from robotcode.robot.utils.stubs import HasError, HasErrors, HeaderAndBodyBlock @@ -16,7 +17,6 @@ from ...common.parts.diagnostics import DiagnosticsResult from ...common.text_document import TextDocument from ..configuration import AnalysisConfig -from ..diagnostics.entities import ArgumentDefinition from ..diagnostics.namespace import Namespace if TYPE_CHECKING: diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/hover.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/hover.py index b0dabd2d4..8d2510b97 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/hover.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/hover.py @@ -1,12 +1,9 @@ -from __future__ import annotations - import ast import asyncio import reprlib from typing import ( TYPE_CHECKING, Any, - Awaitable, Callable, List, Optional, @@ -15,6 +12,9 @@ cast, ) +from robot.parsing.lexer.tokens import Token +from robot.parsing.model.blocks import TestCase, TestCaseSection +from robot.parsing.model.statements import Documentation, Tags from robotcode.core.lsp.types import Hover, MarkupContent, MarkupKind, Position, Range from robotcode.core.utils.logging import LoggingDescriptor from robotcode.robot.utils.ast import get_nodes_at_position, range_from_node, range_from_token @@ -28,13 +28,13 @@ if TYPE_CHECKING: from ..protocol import RobotLanguageServerProtocol -_HoverMethod = Callable[[ast.AST, List[ast.AST], TextDocument, Position], Awaitable[Optional[Hover]]] +_HoverMethod = Callable[[ast.AST, List[ast.AST], TextDocument, Position], Optional[Hover]] class RobotHoverProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin): _logger = LoggingDescriptor() - def __init__(self, parent: RobotLanguageServerProtocol) -> None: + def __init__(self, parent: "RobotLanguageServerProtocol") -> None: super().__init__(parent) parent.hover.collect.add(self.collect) @@ -56,7 +56,7 @@ def _find_method(self, cls: Type[Any]) -> Optional[_HoverMethod]: @language_id("robotframework") @_logger.call - async def collect(self, sender: Any, document: TextDocument, position: Position) -> Optional[Hover]: + def collect(self, sender: Any, document: TextDocument, position: Position) -> Optional[Hover]: model = self.parent.documents_cache.get_model(document) result_nodes = get_nodes_at_position(model, position) @@ -64,20 +64,20 @@ async def collect(self, sender: Any, document: TextDocument, position: Position) if not result_nodes: return None - result = await self._hover_default(result_nodes, document, position) + result = self._hover_default(result_nodes, document, position) if result: return result for result_node in reversed(result_nodes): method = self._find_method(type(result_node)) if method is not None: - result = await method(result_node, result_nodes, document, position) + result = method(result_node, result_nodes, document, position) if result is not None: return result return None - async def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: Position) -> Optional[Hover]: + def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: Position) -> Optional[Hover]: namespace = self.parent.documents_cache.get_namespace(document) all_variable_refs = namespace.get_variable_references() @@ -171,19 +171,15 @@ async def _hover_default(self, nodes: List[ast.AST], document: TextDocument, pos return None - async def hover_TestCase( # noqa: N802 + def hover_TestCase( # noqa: N802 self, node: ast.AST, nodes: List[ast.AST], document: TextDocument, position: Position ) -> Optional[Hover]: - from robot.parsing.lexer.tokens import Token as RobotToken - from robot.parsing.model.blocks import TestCase, TestCaseSection - from robot.parsing.model.statements import Documentation, Tags - test_case = cast(TestCase, node) if not position.is_in_range(range_from_node(test_case.header)): return None - name_token = cast(RobotToken, test_case.header.get_token(RobotToken.TESTCASE_NAME)) + name_token = test_case.header.get_token(Token.TESTCASE_NAME) if name_token is None: return None diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/inlay_hint.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/inlay_hint.py index 849a63e8e..58bcd9243 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/inlay_hint.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/inlay_hint.py @@ -7,12 +7,12 @@ from robot.parsing.lexer.tokens import Token from robotcode.core.lsp.types import InlayHint, InlayHintKind, Range from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.library_doc import KeywordArgumentKind, KeywordDoc, LibraryDoc from robotcode.robot.utils.ast import iter_nodes, range_from_node, range_from_token from ...common.decorators import language_id from ...common.text_document import TextDocument from ..configuration import InlayHintsConfig -from ..diagnostics.library_doc import KeywordArgumentKind, KeywordDoc, LibraryDoc from ..diagnostics.namespace import Namespace if TYPE_CHECKING: diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/references.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/references.py index 1d23f4048..30109a4e3 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/references.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/references.py @@ -19,24 +19,24 @@ from robotcode.core.utils.caching import SimpleLRUCache from robotcode.core.utils.logging import LoggingDescriptor from robotcode.core.utils.threading import threaded -from robotcode.robot.utils import get_robot_version -from robotcode.robot.utils.ast import get_nodes_at_position, get_tokens_at_position, iter_nodes, range_from_token -from robotcode.robot.utils.match import normalize - -from ...common.decorators import language_id -from ...common.text_document import TextDocument -from ..diagnostics.entities import ( +from robotcode.robot.diagnostics.entities import ( LibraryEntry, LocalVariableDefinition, ResourceEntry, VariableDefinition, ) -from ..diagnostics.library_doc import ( +from robotcode.robot.diagnostics.library_doc import ( RESOURCE_FILE_EXTENSION, ROBOT_FILE_EXTENSION, KeywordDoc, LibraryDoc, ) +from robotcode.robot.utils import get_robot_version +from robotcode.robot.utils.ast import get_nodes_at_position, get_tokens_at_position, iter_nodes, range_from_token +from robotcode.robot.utils.match import normalize + +from ...common.decorators import language_id +from ...common.text_document import TextDocument from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart @@ -274,7 +274,7 @@ def find_keyword_references_in_file( return [] - async def has_cached_keyword_references( + def has_cached_keyword_references( self, document: TextDocument, kw_doc: KeywordDoc, include_declaration: bool = True ) -> bool: return self._keyword_reference_cache.has(document, kw_doc, include_declaration, False) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/rename.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/rename.py index c0badcd36..78d655019 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/rename.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/rename.py @@ -19,13 +19,13 @@ WorkspaceEdit, ) from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.entities import VariableDefinition, VariableDefinitionType +from robotcode.robot.diagnostics.library_doc import KeywordDoc from robotcode.robot.utils.ast import get_nodes_at_position, get_tokens_at_position, range_from_token from ...common.decorators import language_id from ...common.parts.rename import CantRenameError from ...common.text_document import TextDocument -from ..diagnostics.entities import VariableDefinition, VariableDefinitionType -from ..diagnostics.library_doc import KeywordDoc from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/robot_workspace.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/robot_workspace.py index 116656201..14a3af54c 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/robot_workspace.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/robot_workspace.py @@ -25,7 +25,7 @@ AnalysisConfig, RobotCodeConfig, ) -from robotcode.language_server.robotframework.diagnostics.library_doc import ( +from robotcode.robot.diagnostics.library_doc import ( RESOURCE_FILE_EXTENSION, ROBOT_FILE_EXTENSION, ) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/semantic_tokens.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/semantic_tokens.py index 8012cacc5..6eec025af 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/semantic_tokens.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/semantic_tokens.py @@ -49,13 +49,7 @@ SemanticTokenTypes, ) from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.utils import get_robot_version -from robotcode.robot.utils.ast import iter_over_keyword_names_and_owners, token_in_range -from robotcode.robot.utils.visitor import iter_nodes - -from ...common.decorators import language_id -from ...common.text_document import TextDocument, range_to_utf16 -from ..diagnostics.library_doc import ( +from robotcode.robot.diagnostics.library_doc import ( ALL_RUN_KEYWORDS_MATCHERS, BUILTIN_LIBRARY_NAME, KeywordArgumentKind, @@ -63,6 +57,12 @@ KeywordMatcher, LibraryDoc, ) +from robotcode.robot.utils import get_robot_version +from robotcode.robot.utils.ast import iter_over_keyword_names_and_owners, token_in_range +from robotcode.robot.utils.visitor import iter_nodes + +from ...common.decorators import language_id +from ...common.text_document import TextDocument, range_to_utf16 from ..diagnostics.model_helper import ModelHelperMixin from ..diagnostics.namespace import DEFAULT_BDD_PREFIXES, Namespace from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/signature_help.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/signature_help.py index 2f395cf0d..45cfe5a1c 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/signature_help.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/signature_help.py @@ -16,11 +16,11 @@ SignatureInformation, ) from robotcode.core.utils.logging import LoggingDescriptor +from robotcode.robot.diagnostics.library_doc import KeywordDoc, LibraryDoc from robotcode.robot.utils.ast import get_node_at_position, get_tokens_at_position, range_from_token from ...common.decorators import language_id, retrigger_characters, trigger_characters from ...common.text_document import TextDocument -from ..diagnostics.library_doc import KeywordDoc, LibraryDoc from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart diff --git a/packages/robot/src/robotcode/robot/diagnostics/__init__.py b/packages/robot/src/robotcode/robot/diagnostics/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/entities.py b/packages/robot/src/robotcode/robot/diagnostics/entities.py similarity index 99% rename from packages/language_server/src/robotcode/language_server/robotframework/diagnostics/entities.py rename to packages/robot/src/robotcode/robot/diagnostics/entities.py index 160c0f529..31ecb8030 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/entities.py +++ b/packages/robot/src/robotcode/robot/diagnostics/entities.py @@ -2,12 +2,13 @@ from enum import Enum from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, TypeVar, cast -from robot.parsing.lexer.tokens import Token from robotcode.core.lsp.types import Position, Range from robotcode.robot.utils.ast import range_from_token +from robot.parsing.lexer.tokens import Token + if TYPE_CHECKING: - from .library_doc import KeywordDoc, LibraryDoc + from robotcode.robot.diagnostics.library_doc import KeywordDoc, LibraryDoc _F = TypeVar("_F", bound=Callable[..., Any]) @@ -131,9 +132,10 @@ class InvalidVariableError(Exception): class VariableMatcher: def __init__(self, name: str) -> None: - from robot.variables.search import search_variable from robotcode.robot.utils.match import normalize + from robot.variables.search import search_variable + self.name = name match = search_variable(name, "$@&%", ignore_errors=True) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/library_doc.py b/packages/robot/src/robotcode/robot/diagnostics/library_doc.py similarity index 99% rename from packages/language_server/src/robotcode/language_server/robotframework/diagnostics/library_doc.py rename to packages/robot/src/robotcode/robot/diagnostics/library_doc.py index 487133d2d..c9f7cc921 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/diagnostics/library_doc.py +++ b/packages/robot/src/robotcode/robot/diagnostics/library_doc.py @@ -33,15 +33,8 @@ cast, ) -from robot.parsing.lexer.tokens import Token from robotcode.core.lsp.types import Position, Range -from robotcode.robot.utils import get_robot_version -from robotcode.robot.utils.ast import get_variable_token, range_from_token, strip_variable_token -from robotcode.robot.utils.markdownformatter import MarkDownFormatter -from robotcode.robot.utils.match import normalize, normalize_namespace -from robotcode.robot.utils.stubs import HasError, HasErrors - -from .entities import ( +from robotcode.robot.diagnostics.entities import ( ArgumentDefinition, ImportedVariableDefinition, LibraryArgumentDefinition, @@ -49,6 +42,13 @@ SourceEntity, single_call, ) +from robotcode.robot.utils import get_robot_version +from robotcode.robot.utils.ast import get_variable_token, range_from_token, strip_variable_token +from robotcode.robot.utils.markdownformatter import MarkDownFormatter +from robotcode.robot.utils.match import normalize, normalize_namespace +from robotcode.robot.utils.stubs import HasError, HasErrors + +from robot.parsing.lexer.tokens import Token RUN_KEYWORD_NAMES = [ "Run Keyword", diff --git a/tests/robotcode/language_server/robotframework/parts/test_hover.py b/tests/robotcode/language_server/robotframework/parts/test_hover.py index 3634998be..a39a2b035 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_hover.py +++ b/tests/robotcode/language_server/robotframework/parts/test_hover.py @@ -1,4 +1,3 @@ -import asyncio from pathlib import Path from typing import Optional @@ -55,13 +54,10 @@ async def test( test_document: TextDocument, data: GeneratedTestData, ) -> None: - result = await asyncio.wait_for( - protocol.robot_hover.collect( - protocol.hover, - test_document, - Position(line=data.line, character=data.character), - ), - 60, + result = protocol.robot_hover.collect( + protocol.hover, + test_document, + Position(line=data.line, character=data.character), ) regtest.write(yaml.dump({"data": data, "result": split(result)}))