diff --git a/cliff.toml b/cliff.toml index a8057fea7..5349d3231 100644 --- a/cliff.toml +++ b/cliff.toml @@ -78,7 +78,7 @@ commit_parsers = [ { message = "^doc", group = "Documentation" }, { message = "^perf", group = "Performance" }, { message = "^refactor", group = "Refactor" }, - { message = "^style", group = "Styling" }, + { message = "^style", group = "Styling", skip = true }, { message = "^test", group = "Testing" }, { message = "^chore\\(release\\): prepare for", skip = true }, { message = "^chore\\(deps\\)", skip = true }, diff --git a/hatch.toml b/hatch.toml index 919901113..26ec5e64d 100644 --- a/hatch.toml +++ b/hatch.toml @@ -109,6 +109,7 @@ matrix.rf.dependencies = [ ] [envs.lint] +python = "3.8" #skip-install = true #extra-dependencies = ["tomli>=2.0.0"] extra-dependencies = [ diff --git a/packages/analyze/src/robotcode/analyze/analyzer.py b/packages/analyze/src/robotcode/analyze/analyzer.py index e8fb85b5a..e99275aa5 100644 --- a/packages/analyze/src/robotcode/analyze/analyzer.py +++ b/packages/analyze/src/robotcode/analyze/analyzer.py @@ -6,7 +6,12 @@ class Analyzer: - def __init__(self, config: AnalyzerConfig, robot_config: RobotConfig, root_folder: Path): + def __init__( + self, + config: AnalyzerConfig, + robot_config: RobotConfig, + root_folder: Path, + ): self.config = config self.robot_config = robot_config self.root_folder = root_folder diff --git a/packages/analyze/src/robotcode/analyze/cli.py b/packages/analyze/src/robotcode/analyze/cli.py index 2aa530e11..5c1c83066 100644 --- a/packages/analyze/src/robotcode/analyze/cli.py +++ b/packages/analyze/src/robotcode/analyze/cli.py @@ -3,17 +3,17 @@ import click from robotcode.analyze.config import AnalyzerConfig from robotcode.plugin import Application, pass_application -from robotcode.robot.config.loader import load_config_from_path, load_robot_config_from_path +from robotcode.robot.config.loader import ( + load_config_from_path, + load_robot_config_from_path, +) from robotcode.robot.config.utils import get_config_files from .__version__ import __version__ @click.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, ) @click.version_option( @@ -23,17 +23,17 @@ ) @click.argument("paths", nargs=-1, type=click.Path(exists=True, dir_okay=True)) @pass_application -def analyze( - app: Application, - paths: Tuple[str], -) -> Union[str, int, None]: +def analyze(app: Application, paths: Tuple[str]) -> Union[str, int, None]: """TODO: Analyzes a Robot Framework project.""" config_files, root_folder, _ = get_config_files(paths, app.config.config_files, verbose_callback=app.verbose) try: analizer_config = load_config_from_path( - AnalyzerConfig, *config_files, tool_name="robotcode-analyze", robot_toml_tool_name="robotcode-analyze" + AnalyzerConfig, + *config_files, + tool_name="robotcode-analyze", + robot_toml_tool_name="robotcode-analyze", ).evaluated() robot_config = ( diff --git a/packages/core/src/robotcode/core/async_tools.py b/packages/core/src/robotcode/core/async_tools.py index 450a66e64..0a732096c 100644 --- a/packages/core/src/robotcode/core/async_tools.py +++ b/packages/core/src/robotcode/core/async_tools.py @@ -90,7 +90,10 @@ async def __aiter__(self) -> AsyncIterator[_TCallable]: yield r async def _notify( - self, *args: Any, callback_filter: Optional[Callable[[_TCallable], bool]] = None, **kwargs: Any + self, + *args: Any, + callback_filter: Optional[Callable[[_TCallable], bool]] = None, + **kwargs: Any, ) -> AsyncIterator[_TResult]: for method in filter( lambda x: callback_filter(x) if callback_filter is not None else True, @@ -118,7 +121,11 @@ async def __call__(self, *args: Any, **kwargs: Any) -> List[_TResult]: class AsyncEventDescriptorBase(Generic[_TCallable, _TResult, _TEvent]): def __init__( - self, _func: _TCallable, factory: Callable[..., _TEvent], *factory_args: Any, **factory_kwargs: Any + self, + _func: _TCallable, + factory: Callable[..., _TEvent], + *factory_args: Any, + **factory_kwargs: Any, ) -> None: self._func = _func self.__factory = factory @@ -137,7 +144,11 @@ def __get__(self, obj: Any, objtype: Type[Any]) -> _TEvent: name = f"__async_event_{self._func.__name__}__" if not hasattr(obj, name): - setattr(obj, name, self.__factory(*self.__factory_args, **self.__factory_kwargs)) + setattr( + obj, + name, + self.__factory(*self.__factory_args, **self.__factory_kwargs), + ) return cast("_TEvent", getattr(obj, name)) @@ -210,7 +221,9 @@ def __call__(self, *args: Any, **kwargs: Any) -> AsyncIterator[Union[_TResult, B return self._notify(*args, **kwargs) -def _get_name_prefix(descriptor: AsyncEventDescriptorBase[Any, Any, Any]) -> str: +def _get_name_prefix( + descriptor: AsyncEventDescriptorBase[Any, Any, Any], +) -> str: if descriptor._owner is None: return type(descriptor).__qualname__ @@ -227,13 +240,19 @@ class async_tasking_event_iterator( # noqa: N801 ): def __init__(self, _func: _TCallable) -> None: super().__init__( - _func, AsyncTaskingEventIterator[_TCallable, Any], task_name_prefix=lambda: _get_name_prefix(self) + _func, + AsyncTaskingEventIterator[_TCallable, Any], + task_name_prefix=lambda: _get_name_prefix(self), ) class async_tasking_event(AsyncEventDescriptorBase[_TCallable, Any, AsyncTaskingEvent[_TCallable, Any]]): # noqa: N801 def __init__(self, _func: _TCallable) -> None: - super().__init__(_func, AsyncTaskingEvent[_TCallable, Any], task_name_prefix=lambda: _get_name_prefix(self)) + super().__init__( + _func, + AsyncTaskingEvent[_TCallable, Any], + task_name_prefix=lambda: _get_name_prefix(self), + ) async def check_canceled() -> bool: @@ -418,7 +437,10 @@ async def __aenter__(self) -> None: await self.acquire() async def __aexit__( - self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType] + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], ) -> None: self.release() @@ -568,7 +590,10 @@ def get_current_future_info() -> Optional[FutureInfo]: def create_sub_task( - coro: Coroutine[Any, Any, _T], *, name: Optional[str] = None, loop: Optional[asyncio.AbstractEventLoop] = None + coro: Coroutine[Any, Any, _T], + *, + name: Optional[str] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, ) -> asyncio.Task[_T]: ct = get_current_future_info() @@ -578,7 +603,9 @@ def create_sub_task( else: async def create_task( - lo: asyncio.AbstractEventLoop, c: Coroutine[Any, Any, _T], n: Optional[str] + lo: asyncio.AbstractEventLoop, + c: Coroutine[Any, Any, _T], + n: Optional[str], ) -> asyncio.Task[_T]: return create_sub_task(c, name=n, loop=lo) @@ -593,7 +620,9 @@ async def create_task( return result -def create_sub_future(loop: Optional[asyncio.AbstractEventLoop] = None) -> asyncio.Future[Any]: +def create_sub_future( + loop: Optional[asyncio.AbstractEventLoop] = None, +) -> asyncio.Future[Any]: ct = get_current_future_info() if loop is None: diff --git a/packages/core/src/robotcode/core/concurrent.py b/packages/core/src/robotcode/core/concurrent.py index 6b9105018..196d6364b 100644 --- a/packages/core/src/robotcode/core/concurrent.py +++ b/packages/core/src/robotcode/core/concurrent.py @@ -1,7 +1,18 @@ import inspect from concurrent.futures import CancelledError, Future from threading import Event, RLock, Thread, current_thread, local -from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, TypeVar, cast, overload +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Optional, + Tuple, + TypeVar, + cast, + overload, +) from typing_extensions import ParamSpec @@ -70,7 +81,10 @@ def __init__(self) -> None: def _run_callable_in_thread_handler( - future: FutureEx[_TResult], callable: Callable[..., _TResult], args: Tuple[Any, ...], kwargs: Dict[str, Any] + future: FutureEx[_TResult], + callable: Callable[..., _TResult], + args: Tuple[Any, ...], + kwargs: Dict[str, Any], ) -> None: if not future.set_running_or_notify_cancel(): return @@ -106,7 +120,7 @@ def check_current_thread_canceled(at_least_seconds: Optional[float] = None, rais if raise_exception: name = current_thread().name - raise CancelledError(f"Thread {name+' ' if name else ' '}cancelled") + raise CancelledError(f"Thread {name + ' ' if name else ' '}cancelled") return True @@ -127,7 +141,9 @@ def run_in_thread(callable: Callable[_P, _TResult], *args: _P.args, **kwargs: _P future: FutureEx[_TResult] = FutureEx() with _running_callables_lock: thread = Thread( - target=_run_callable_in_thread_handler, args=(future, callable, args, kwargs), name=str(callable) + target=_run_callable_in_thread_handler, + args=(future, callable, args, kwargs), + name=str(callable), ) _running_callables[future] = thread future.add_done_callback(_remove_future_from_running_callables) diff --git a/packages/core/src/robotcode/core/event.py b/packages/core/src/robotcode/core/event.py index 285743603..1b293943b 100644 --- a/packages/core/src/robotcode/core/event.py +++ b/packages/core/src/robotcode/core/event.py @@ -138,7 +138,11 @@ def __get__(self, obj: Any, objtype: Type[Any]) -> _TEvent: name = f"__event_{self._func.__name__}__" if not hasattr(obj, name): - setattr(obj, name, self.__factory(*self.__factory_args, **self.__factory_kwargs)) + setattr( + obj, + name, + self.__factory(*self.__factory_args, **self.__factory_kwargs), + ) return cast("_TEvent", getattr(obj, name)) diff --git a/packages/core/src/robotcode/core/lsp/types.py b/packages/core/src/robotcode/core/lsp/types.py index 7de0fc8f4..fba707ed9 100644 --- a/packages/core/src/robotcode/core/lsp/types.py +++ b/packages/core/src/robotcode/core/lsp/types.py @@ -3453,7 +3453,8 @@ class CodeLens(CamelSnakeMixin): source text, like the number of references, a way to run tests, etc. A code lens is _unresolved_ when no command is associated to it. For performance - reasons the creation of a code lens and resolving should be done in two stages.""" + reasons the creation of a code lens and resolving should be done in two stages. + """ range: Range """The range in which this code lens is valid. Should only span a single line.""" @@ -3944,27 +3945,15 @@ def __iter__(self) -> Iterator[Position]: @staticmethod def zero() -> Range: return Range( - start=Position( - line=0, - character=0, - ), - end=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), + end=Position(line=0, character=0), ) @staticmethod def invalid() -> Range: return Range( - start=Position( - line=-1, - character=-1, - ), - end=Position( - line=-1, - character=-1, - ), + start=Position(line=-1, character=-1), + end=Position(line=-1, character=-1), ) def extend( @@ -4835,7 +4824,11 @@ class ServerCapabilities(CamelSnakeMixin): # Since: 3.16.0 linked_editing_range_provider: Optional[ - Union[bool, LinkedEditingRangeOptions, LinkedEditingRangeRegistrationOptions] + Union[ + bool, + LinkedEditingRangeOptions, + LinkedEditingRangeRegistrationOptions, + ] ] = None """The server provides linked editing range support. @@ -7164,7 +7157,10 @@ class TextDocumentColorPresentationOptions(CamelSnakeMixin): # Since: 3.17.0 -DocumentDiagnosticReport = Union[RelatedFullDocumentDiagnosticReport, RelatedUnchangedDocumentDiagnosticReport] +DocumentDiagnosticReport = Union[ + RelatedFullDocumentDiagnosticReport, + RelatedUnchangedDocumentDiagnosticReport, +] """The result of a document diagnostic pull request. A report can either be a full report containing all diagnostics for the requested document or an unchanged report indicating that nothing @@ -7198,7 +7194,8 @@ class PrepareRenameResultType2(CamelSnakeMixin): WorkspaceDocumentDiagnosticReport = Union[ - WorkspaceFullDocumentDiagnosticReport, WorkspaceUnchangedDocumentDiagnosticReport + WorkspaceFullDocumentDiagnosticReport, + WorkspaceUnchangedDocumentDiagnosticReport, ] """A workspace diagnostic document report. diff --git a/packages/core/src/robotcode/core/uri.py b/packages/core/src/robotcode/core/uri.py index b427da77b..e0cd5c6e5 100644 --- a/packages/core/src/robotcode/core/uri.py +++ b/packages/core/src/robotcode/core/uri.py @@ -31,7 +31,16 @@ def __iter__(self) -> Iterator[str]: yield from astuple(self) def __hash__(self) -> int: - return hash((self.scheme, self.netloc, self.path, self.params, self.query, self.fragment)) + return hash( + ( + self.scheme, + self.netloc, + self.path, + self.params, + self.query, + self.fragment, + ) + ) class Uri(Mapping[str, str]): diff --git a/packages/core/src/robotcode/core/utils/caching.py b/packages/core/src/robotcode/core/utils/caching.py index 4cfdaefda..1d8901b4d 100644 --- a/packages/core/src/robotcode/core/utils/caching.py +++ b/packages/core/src/robotcode/core/utils/caching.py @@ -57,7 +57,10 @@ def get(self, func: Callable[..., _T], *args: Any, **kwargs: Any) -> _T: @staticmethod def _make_key(*args: Any, **kwargs: Any) -> Tuple[Any, ...]: - return (tuple(_freeze(v) for v in args), hash(frozenset({k: _freeze(v) for k, v in kwargs.items()}))) + return ( + tuple(_freeze(v) for v in args), + hash(frozenset({k: _freeze(v) for k, v in kwargs.items()})), + ) def clear(self) -> None: with self._lock: diff --git a/packages/core/src/robotcode/core/utils/cli.py b/packages/core/src/robotcode/core/utils/cli.py index f97168b65..76ab69fce 100644 --- a/packages/core/src/robotcode/core/utils/cli.py +++ b/packages/core/src/robotcode/core/utils/cli.py @@ -2,4 +2,7 @@ def show_hidden_arguments() -> bool: - return os.environ.get("ROBOTCODE_SHOW_HIDDEN_ARGS", "").lower() not in ["true", "1"] + return os.environ.get("ROBOTCODE_SHOW_HIDDEN_ARGS", "").lower() not in [ + "true", + "1", + ] diff --git a/packages/core/src/robotcode/core/utils/dataclasses.py b/packages/core/src/robotcode/core/utils/dataclasses.py index 7c1f4ae80..0a548afb9 100644 --- a/packages/core/src/robotcode/core/utils/dataclasses.py +++ b/packages/core/src/robotcode/core/utils/dataclasses.py @@ -74,10 +74,7 @@ def to_camel_case(s: str) -> str: if not s: result = s else: - result = str(s[0]).lower() + _RE_CAMEL_CASE_2.sub( - lambda matched: str(matched.group(1)).upper(), - s[1:], - ) + result = str(s[0]).lower() + _RE_CAMEL_CASE_2.sub(lambda matched: str(matched.group(1)).upper(), s[1:]) __to_snake_camel_cache[s] = result return cast(str, result) @@ -157,7 +154,7 @@ def _decode_case_for_member_name(type: Type[Any], name: str) -> str: def get_dataclass_fields(t: Type[Any]) -> Tuple[dataclasses.Field, ...]: # type: ignore - fields = __dataclasses_cache.get(t, None) + fields = __dataclasses_cache.get(t) if fields is None: fields = __dataclasses_cache[t] = dataclasses.fields(t) return fields @@ -330,13 +327,27 @@ def __from_dict_handle_mapping(value: Any, t: Type[Any], strict: bool) -> Tuple[ return None, False -__from_dict_handlers: List[Tuple[Callable[[Type[Any]], bool], Callable[[Any, Type[Any], bool], Tuple[Any, bool]]]] = [ - (lambda t: t in {int, bool, float, str, NONETYPE}, __from_dict_handle_basic_types), +__from_dict_handlers: List[ + Tuple[ + Callable[[Type[Any]], bool], + Callable[[Any, Type[Any], bool], Tuple[Any, bool]], + ] +] = [ + ( + lambda t: t in {int, bool, float, str, NONETYPE}, + __from_dict_handle_basic_types, + ), (lambda t: _get_origin_cached(t) is Union, __from_dict_handle_union), (lambda t: _get_origin_cached(t) is Literal, __from_dict_handle_literal), (__is_enum, __from_dict_handle_enum), - (lambda t: is_subclass_cached(_get_origin_cached(t) or t, Sequence), __from_dict_handle_sequence), - (lambda t: is_subclass_cached(_get_origin_cached(t) or t, Mapping), __from_dict_handle_mapping), + ( + lambda t: is_subclass_cached(_get_origin_cached(t) or t, Sequence), + __from_dict_handle_sequence, + ), + ( + lambda t: is_subclass_cached(_get_origin_cached(t) or t, Mapping), + __from_dict_handle_mapping, + ), (lambda t: t is Any or t is Ellipsis, lambda v, _t, _: (v, True)), # type: ignore ] @@ -346,7 +357,7 @@ def __from_dict_handle_mapping(value: Any, t: Type[Any], strict: bool) -> Tuple[ def __get_non_default_parameter(t: Type[Any], signature: inspect.Signature) -> Set[str]: - r = __non_default_parameters_cache.get(t, None) + r = __non_default_parameters_cache.get(t) if r is None: r = __non_default_parameters_cache[t] = { k for k, v in signature.parameters.items() if v.default == inspect.Parameter.empty @@ -358,7 +369,7 @@ def __get_non_default_parameter(t: Type[Any], signature: inspect.Signature) -> S def __get_signature_keys_cached(t: Type[Any], signature: inspect.Signature) -> Set[str]: - r = __signature_keys_cache.get(t, None) + r = __signature_keys_cache.get(t) if r is None: r = __signature_keys_cache[t] = set(signature.parameters.keys()) return r @@ -427,7 +438,7 @@ def from_dict( same_keys = cased_value.keys() & sig_keys - if strict and any(k for k in cased_value.keys() if k not in sig_keys): + if strict and any(k for k in cased_value if k not in sig_keys): continue if not all(k in same_keys for k in non_default_parameters): @@ -442,7 +453,7 @@ def from_dict( elif match_same_keys is not None and len(match_same_keys) == len(same_keys): raise TypeError( f"Value {value!r} matches to more then one types of " - f"{repr(types[0].__name__) if len(types)==1 else ' | '.join(repr(e.__name__) for e in types)}." + f"{repr(types[0].__name__) if len(types) == 1 else ' | '.join(repr(e.__name__) for e in types)}." ) if ( @@ -492,12 +503,7 @@ def from_json( return from_dict(json.loads(s), types, strict=strict) -def as_dict( - value: Any, - *, - remove_defaults: bool = False, - encode: bool = True, -) -> Dict[str, Any]: +def as_dict(value: Any, *, remove_defaults: bool = False, encode: bool = True) -> Dict[str, Any]: if not dataclasses.is_dataclass(value): raise TypeError("as_dict() should be called on dataclass instances") @@ -510,7 +516,7 @@ def _handle_basic_types(value: Any, _remove_defaults: bool, _encode: bool) -> An def _handle_dataclass(value: Any, remove_defaults: bool, encode: bool) -> Dict[str, Any]: t = type(value) - fields = __dataclasses_cache.get(t, None) + fields = __dataclasses_cache.get(t) if fields is None: fields = dataclasses.fields(t) __dataclasses_cache[t] = fields @@ -553,10 +559,7 @@ def _as_dict_handle_unknown_type(value: Any, _remove_defaults: bool, _encode: bo lambda value: type(value) in {int, bool, float, str, NONETYPE}, _handle_basic_types, ), - ( - lambda value: dataclasses.is_dataclass(value), - _handle_dataclass, - ), + (dataclasses.is_dataclass, _handle_dataclass), (lambda value: isinstance(value, enum.Enum), _as_dict_handle_enum), ( lambda value: (isinstance(value, tuple) and hasattr(value, "_fields")), @@ -566,24 +569,14 @@ def _as_dict_handle_unknown_type(value: Any, _remove_defaults: bool, _encode: bo lambda value: isinstance(value, (list, tuple, set, frozenset)), _as_dict_handle_sequence, ), - ( - lambda value: isinstance(value, dict), - _as_dict_handle_dict, - ), - ( - lambda _value: True, - _as_dict_handle_unknown_type, - ), + (lambda value: isinstance(value, dict), _as_dict_handle_dict), + (lambda _value: True, _as_dict_handle_unknown_type), ] -def _as_dict_inner( - value: Any, - remove_defaults: bool, - encode: bool, -) -> Any: +def _as_dict_inner(value: Any, remove_defaults: bool, encode: bool) -> Any: t = type(value) - func = __handlers_cache.get(t, None) + func = __handlers_cache.get(t) if func is None: if t in __handlers_cache: return __handlers_cache[t](value, remove_defaults, encode) diff --git a/packages/core/src/robotcode/core/utils/inspect.py b/packages/core/src/robotcode/core/utils/inspect.py index 41c4cbf39..42e81c7ea 100644 --- a/packages/core/src/robotcode/core/utils/inspect.py +++ b/packages/core/src/robotcode/core/utils/inspect.py @@ -30,7 +30,9 @@ def is_lambda(v: Any) -> bool: return isinstance(v, _lambda_type) and v.__name__ == _lambda_name -def ensure_coroutine(func: Callable[..., Any]) -> Callable[..., Coroutine[Any, Any, Any]]: +def ensure_coroutine( + func: Callable[..., Any], +) -> Callable[..., Coroutine[Any, Any, Any]]: async def wrapper(*fargs: Any, **fkwargs: Any) -> Any: return func(*fargs, **fkwargs) diff --git a/packages/core/src/robotcode/core/utils/logging.py b/packages/core/src/robotcode/core/utils/logging.py index 78ef46b62..524b45ab0 100644 --- a/packages/core/src/robotcode/core/utils/logging.py +++ b/packages/core/src/robotcode/core/utils/logging.py @@ -40,7 +40,9 @@ def _repr(o: Any) -> str: # return repr(o) -def get_class_that_defined_method(meth: Callable[..., Any]) -> Optional[Type[Any]]: +def get_class_that_defined_method( + meth: Callable[..., Any], +) -> Optional[Type[Any]]: if inspect.ismethod(meth): for c in inspect.getmro(meth.__self__.__class__): if c.__dict__.get(meth.__name__) is meth: @@ -191,7 +193,13 @@ def log( **kwargs: Any, ) -> None: if self.is_enabled_for(level) and condition is not None and condition() or condition is None: - self.logger.log(level, msg() if callable(msg) else msg, *args, stacklevel=stacklevel, **kwargs) + self.logger.log( + level, + msg() if callable(msg) else msg, + *args, + stacklevel=stacklevel, + **kwargs, + ) def debug( self, @@ -201,7 +209,14 @@ def debug( stacklevel: int = 3, **kwargs: Any, ) -> None: - return self.log(logging.DEBUG, msg, condition, *args, stacklevel=stacklevel, **kwargs) + return self.log( + logging.DEBUG, + msg, + condition, + *args, + stacklevel=stacklevel, + **kwargs, + ) def info( self, @@ -221,7 +236,14 @@ def warning( stacklevel: int = 3, **kwargs: Any, ) -> None: - return self.log(logging.WARNING, msg, condition, *args, stacklevel=stacklevel, **kwargs) + return self.log( + logging.WARNING, + msg, + condition, + *args, + stacklevel=stacklevel, + **kwargs, + ) def error( self, @@ -231,7 +253,14 @@ def error( stacklevel: int = 3, **kwargs: Any, ) -> None: - return self.log(logging.ERROR, msg, condition, *args, stacklevel=stacklevel, **kwargs) + return self.log( + logging.ERROR, + msg, + condition, + *args, + stacklevel=stacklevel, + **kwargs, + ) def trace( self, @@ -286,7 +315,14 @@ def critical( stacklevel: int = 3, **kwargs: Any, ) -> None: - return self.log(logging.CRITICAL, msg, condition, *args, stacklevel=stacklevel, **kwargs) + return self.log( + logging.CRITICAL, + msg, + condition, + *args, + stacklevel=stacklevel, + **kwargs, + ) def is_enabled_for(self, level: int) -> bool: return self.logger.isEnabledFor(level) @@ -371,7 +407,11 @@ def _decorator(func: _F) -> Callable[[_F], _F]: level=level, prefix=prefix, condition=condition, - states={CallState.ENTERING: entering, CallState.EXITING: exiting, CallState.EXCEPTION: exception}, + states={ + CallState.ENTERING: entering, + CallState.EXITING: exiting, + CallState.EXCEPTION: exception, + }, ) ) @@ -446,11 +486,13 @@ def build_enter_message() -> str: def build_exit_message(res: Any, duration: Optional[float]) -> str: return "{0}(...) -> {1}{2}".format( - func_name(), _repr(res), f" duration: {duration}" if duration is not None else "" + func_name(), + _repr(res), + f" duration: {duration}" if duration is not None else "", ) def build_exception_message(exception: BaseException) -> str: - return "{0}(...) -> {1}: {2}".format(func_name(), type(exception).__qualname__, exception) + return f"{func_name()}(...) -> {type(exception).__qualname__}: {exception}" _log(build_enter_message, state=CallState.ENTERING) @@ -488,6 +530,9 @@ def _empty__decorator(func: _F) -> Callable[[_F], _F]: return func if _func is None: - return cast(Callable[[_F], _F], _decorator if type(self)._call_tracing_enabled else _empty__decorator) + return cast( + Callable[[_F], _F], + _decorator if type(self)._call_tracing_enabled else _empty__decorator, + ) return _decorator(_func) if type(self)._call_tracing_enabled else _empty__decorator(_func) diff --git a/packages/core/src/robotcode/core/utils/path.py b/packages/core/src/robotcode/core/utils/path.py index 358d05325..8f9d98bb8 100644 --- a/packages/core/src/robotcode/core/utils/path.py +++ b/packages/core/src/robotcode/core/utils/path.py @@ -5,7 +5,10 @@ from typing import Any, Union -def path_is_relative_to(path: Union[Path, str, PathLike[Any]], other_path: Union[Path, str, PathLike[Any]]) -> bool: +def path_is_relative_to( + path: Union[Path, str, PathLike[Any]], + other_path: Union[Path, str, PathLike[Any]], +) -> bool: try: Path(path).relative_to(other_path) return True diff --git a/packages/debugger/src/robotcode/debugger/cli.py b/packages/debugger/src/robotcode/debugger/cli.py index 921156c57..e9c6c8bab 100644 --- a/packages/debugger/src/robotcode/debugger/cli.py +++ b/packages/debugger/src/robotcode/debugger/cli.py @@ -4,11 +4,11 @@ import click from robotcode.core.types import ServerMode from robotcode.plugin import Application, UnknownError, pass_application -from robotcode.plugin.click_helper.options import resolve_server_options, server_options -from robotcode.plugin.click_helper.types import ( - AddressesPort, - add_options, +from robotcode.plugin.click_helper.options import ( + resolve_server_options, + server_options, ) +from robotcode.plugin.click_helper.types import AddressesPort, add_options from .__version__ import __version__ @@ -17,13 +17,15 @@ @click.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, ) -@click.option("--debug / --no-debug", default=True, help="Enable/disable debug mode", show_default=True) +@click.option( + "--debug / --no-debug", + default=True, + help="Enable/disable debug mode", + show_default=True, +) @click.option( "--stop-on-entry / --no-stop-on-entry", is_flag=True, @@ -138,7 +140,17 @@ def debug( from .run import run_debugger mode, port, bind, pipe_name = resolve_server_options( - ctx, app, mode, port, bind, pipe_name, tcp, None, None, None, pipe_server + ctx, + app, + mode, + port, + bind, + pipe_name, + tcp, + None, + None, + None, + pipe_server, ) app.verbose(f"Debug Mode: {debug}") diff --git a/packages/debugger/src/robotcode/debugger/dap_types.py b/packages/debugger/src/robotcode/debugger/dap_types.py index 829bea9ca..17ea5f6ca 100644 --- a/packages/debugger/src/robotcode/debugger/dap_types.py +++ b/packages/debugger/src/robotcode/debugger/dap_types.py @@ -35,7 +35,7 @@ def _next_id() -> int: @dataclass class ProtocolMessage(Model): type: Union[Literal["request", "response", "event"], str] - seq: int = field(default_factory=lambda: _next_id()) + seq: int = field(default_factory=_next_id) @dataclass @@ -148,10 +148,7 @@ def __repr__(self) -> str: # pragma: no cover @dataclass class StoppedEventBody(Model): - reason: Union[ - StoppedReason, - str, - ] + reason: Union[StoppedReason, str] description: Optional[str] = None thread_id: Optional[int] = None preserve_focus_hint: Optional[bool] = None @@ -919,7 +916,11 @@ class VariablePresentationHint(Model): ] ] = None - visibility: Union[Literal["public", "private", "protected", "internal", "final"], str, None] = None + visibility: Union[ + Literal["public", "private", "protected", "internal", "final"], + str, + None, + ] = None @dataclass diff --git a/packages/debugger/src/robotcode/debugger/debugger.py b/packages/debugger/src/robotcode/debugger/debugger.py index 84ec560bf..6e618adc1 100644 --- a/packages/debugger/src/robotcode/debugger/debugger.py +++ b/packages/debugger/src/robotcode/debugger/debugger.py @@ -72,7 +72,9 @@ ) if get_robot_version() >= (7, 0): - from robot.running.invalidkeyword import InvalidKeywordRunner as UserKeywordHandler + from robot.running.invalidkeyword import ( + InvalidKeywordRunner as UserKeywordHandler, + ) else: from robot.running.userkeyword import UserKeywordHandler @@ -245,10 +247,10 @@ class DebugLogger(LoggerApi): # type: ignore[no-redef] def __init__(self) -> None: self.steps: List[Any] = [] - def start_keyword(self, data: "running.Keyword", result: "result.Keyword") -> None: + def start_keyword(self, data: running.Keyword, result: result.Keyword) -> None: self.steps.append(data) - def end_keyword(self, data: "running.Keyword", result: "result.Keyword") -> None: + def end_keyword(self, data: running.Keyword, result: result.Keyword) -> None: self.steps.pop() @@ -336,7 +338,10 @@ def state(self) -> State: def state(self, value: State) -> None: # if state is changed, do nothing and wait a little bit to avoid busy loop - if self._state == State.Paused and value not in [State.Paused, State.CallKeyword]: + if self._state == State.Paused and value not in [ + State.Paused, + State.CallKeyword, + ]: self._variables_cache.clear() self._variables_object_cache.clear() self._evaluate_cache.clear() @@ -396,7 +401,10 @@ def stop(self) -> None: self.send_event( self, ContinuedEvent( - body=ContinuedEventBody(thread_id=self.main_thread.ident, all_threads_continued=True) + body=ContinuedEventBody( + thread_id=self.main_thread.ident, + all_threads_continued=True, + ) ), ) @@ -428,7 +436,10 @@ def next(self, thread_id: int, granularity: Optional[SteppingGranularity] = None raise InvalidThreadIdError(thread_id) with self.condition: - if self.full_stack_frames and self.full_stack_frames[0].type in ["TEST", "SUITE"]: + if self.full_stack_frames and self.full_stack_frames[0].type in [ + "TEST", + "SUITE", + ]: self.requested_state = RequestedState.StepIn else: self.requested_state = RequestedState.Next @@ -451,7 +462,10 @@ def next(self, thread_id: int, granularity: Optional[SteppingGranularity] = None self.condition.notify_all() def step_in( - self, thread_id: int, target_id: Optional[int] = None, granularity: Optional[SteppingGranularity] = None + self, + thread_id: int, + target_id: Optional[int] = None, + granularity: Optional[SteppingGranularity] = None, ) -> None: if self.main_thread is None or thread_id != self.main_thread.ident: raise InvalidThreadIdError(thread_id) @@ -508,10 +522,16 @@ def set_breakpoints( self.breakpoints.pop(path) elif path: self.breakpoints[path] = result = BreakpointsEntry( - tuple(breakpoints) if breakpoints else (), tuple(lines) if lines else () + tuple(breakpoints) if breakpoints else (), + tuple(lines) if lines else (), ) return [ - Breakpoint(id=id(v), source=Source(path=str(path)), verified=True, line=v.line) + Breakpoint( + id=id(v), + source=Source(path=str(path)), + verified=True, + line=v.line, + ) for v in result.breakpoints ] @@ -590,7 +610,12 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str) hit = False try: vars = EXECUTION_CONTEXTS.current.variables.current - hit = bool(internal_evaluate_expression(vars.replace_string(point.condition), vars)) + hit = bool( + internal_evaluate_expression( + vars.replace_string(point.condition), + vars, + ) + ) except (SystemExit, KeyboardInterrupt): raise except BaseException: @@ -647,7 +672,13 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str) ), ) - def process_end_state(self, status: str, filter_id: Set[str], description: str, text: Optional[str]) -> None: + def process_end_state( + self, + status: str, + filter_id: Set[str], + description: str, + text: Optional[str], + ) -> None: if self.state == State.Stopped: return @@ -709,7 +740,10 @@ def wait_for_running(self) -> None: self.send_event( self, ContinuedEvent( - body=ContinuedEventBody(thread_id=self.main_thread.ident, all_threads_continued=True) + body=ContinuedEventBody( + thread_id=self.main_thread.ident, + all_threads_continued=True, + ) ), ) continue @@ -718,14 +752,14 @@ def wait_for_running(self) -> None: def start_output_group(self, name: str, attributes: Dict[str, Any], type: Optional[str] = None) -> None: if self.group_output: - source = attributes.get("source", None) - line_no = attributes.get("lineno", None) + source = attributes.get("source") + line_no = attributes.get("lineno") self.send_event( self, OutputEvent( body=OutputEventBody( - output=f"\u001b[38;5;14m{(type +' ') if type else ''}\u001b[0m{name}\n", + output=f"\u001b[38;5;14m{(type + ' ') if type else ''}\u001b[0m{name}\n", category=OutputCategory.CONSOLE, group=OutputGroup.START, source=Source(path=str(self.map_path_to_client(source))) if source else None, @@ -737,8 +771,8 @@ def start_output_group(self, name: str, attributes: Dict[str, Any], type: Option def end_output_group(self, name: str, attributes: Dict[str, Any], type: Optional[str] = None) -> None: if self.group_output: - source = attributes.get("source", None) - line_no = attributes.get("lineno", None) + source = attributes.get("source") + line_no = attributes.get("lineno") self.send_event( self, @@ -791,7 +825,7 @@ def add_stackframe_entry( self.full_stack_frames.appendleft(result) - if type in ["KEYWORD"] and source is None and line is None and column is None: + if type == "KEYWORD" and source is None and line is None and column is None: return result if type in ["SUITE", "TEST"]: @@ -819,7 +853,7 @@ def remove_stackframe_entry( ) -> None: self.full_stack_frames.popleft() - if type in ["KEYWORD"] and source is None and line is None and column is None: + if type == "KEYWORD" and source is None and line is None and column is None: return if type in ["SUITE", "TEST"]: @@ -839,7 +873,7 @@ def start_suite(self, name: str, attributes: Dict[str, Any]) -> None: self.debug_logger = DebugLogger() LOGGER.register_logger(self.debug_logger) - source = attributes.get("source", None) + source = attributes.get("source") line_no = attributes.get("lineno", 1) longname = attributes.get("longname", "") status = attributes.get("status", "") @@ -865,7 +899,12 @@ def start_suite(self, name: str, attributes: Dict[str, Any]) -> None: self.wait_for_running() elif entry.source: - self.process_start_state(entry.source, entry.line if entry.line is not None else 0, entry.type, status) + self.process_start_state( + entry.source, + entry.line if entry.line is not None else 0, + entry.type, + status, + ) self.wait_for_running() @@ -879,19 +918,19 @@ def end_suite(self, name: str, attributes: Dict[str, Any]) -> None: status, {"failed_suite"}, "Suite failed.", - f"Suite failed{f': {v}' if (v:=attributes.get('message', None)) else ''}", + f"Suite failed{f': {v}' if (v := attributes.get('message')) else ''}", ) self.wait_for_running() - source = attributes.get("source", None) + source = attributes.get("source") line_no = attributes.get("lineno", 1) type = "SUITE" self.remove_stackframe_entry(name, type, source, line_no) def start_test(self, name: str, attributes: Dict[str, Any]) -> None: - source = attributes.get("source", None) + source = attributes.get("source") line_no = attributes.get("lineno", 1) longname = attributes.get("longname", "") status = attributes.get("status", "") @@ -901,7 +940,12 @@ def start_test(self, name: str, attributes: Dict[str, Any]) -> None: entry = self.add_stackframe_entry(name, type, source, line_no, longname=longname) if self.debug and entry.source: - self.process_start_state(entry.source, entry.line if entry.line is not None else 0, entry.type, status) + self.process_start_state( + entry.source, + entry.line if entry.line is not None else 0, + entry.type, + status, + ) self.wait_for_running() @@ -914,12 +958,12 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None: status, {"failed_test"}, "Test failed.", - f"Test failed{f': {v}' if (v:=attributes.get('message', None)) else ''}", + f"Test failed{f': {v}' if (v := attributes.get('message')) else ''}", ) self.wait_for_running() - source = attributes.get("source", None) + source = attributes.get("source") line_no = attributes.get("lineno", 1) longname = attributes.get("longname", "") type = "TEST" @@ -928,11 +972,11 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None: def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None: status = attributes.get("status", "") - source = attributes.get("source", None) - line_no = attributes.get("lineno", None) + source = attributes.get("source") + line_no = attributes.get("lineno") type = attributes.get("type", "KEYWORD") - libname = attributes.get("libname", None) - kwname = attributes.get("kwname", None) + libname = attributes.get("libname") + kwname = attributes.get("kwname") handler: Any = None if type in ["KEYWORD", "SETUP", "TEARDOWN"]: @@ -944,10 +988,17 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None: pass entry = self.add_stackframe_entry( - kwname, type, source, line_no, handler=handler, libname=libname, kwname=kwname, longname=name + str(kwname), + type, + source, + line_no, + handler=handler, + libname=libname, + kwname=kwname, + longname=name, ) - if status == "NOT RUN" and type not in ["IF"]: + if status == "NOT RUN" and type != "IF": return if self.debug and entry.source and entry.line is not None: @@ -1004,7 +1055,10 @@ def _should_run_except(self, branch: Any, error: str) -> bool: return False for pattern in branch.patterns: - if matcher(error, EXECUTION_CONTEXTS.current.variables.replace_string(pattern)): + if matcher( + error, + EXECUTION_CONTEXTS.current.variables.replace_string(pattern), + ): return True return False @@ -1024,7 +1078,7 @@ def is_not_caugthed_by_except(self, message: Optional[str]) -> bool: return True def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None: - type = attributes.get("type", None) + type = attributes.get("type") if self.debug: status = attributes.get("status", "") @@ -1046,10 +1100,10 @@ def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None: self.wait_for_running() - source = attributes.get("source", None) - line_no = attributes.get("lineno", None) + source = attributes.get("source") + line_no = attributes.get("lineno") type = attributes.get("type", "KEYWORD") - kwname = attributes.get("kwname", None) + kwname = attributes.get("kwname") handler: Any = None if type in ["KEYWORD", "SETUP", "TEARDOWN"]: @@ -1060,7 +1114,7 @@ def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None: except BaseException: pass - self.remove_stackframe_entry(kwname, type, source, line_no, handler=handler) + self.remove_stackframe_entry(str(kwname), type, source, line_no, handler=handler) def set_main_thread(self, thread: threading.Thread) -> None: self.main_thread = thread @@ -1068,7 +1122,12 @@ def set_main_thread(self, thread: threading.Thread) -> None: def get_threads(self) -> List[Thread]: main_thread = self.main_thread or threading.main_thread() - return [Thread(id=main_thread.ident if main_thread.ident else 0, name=main_thread.name or "")] + return [ + Thread( + id=main_thread.ident if main_thread.ident else 0, + name=main_thread.name or "", + ) + ] WINDOW_PATH_REGEX: ClassVar = re.compile(r"^(([a-z]:[\\/])|(\\\\)).*$", re.RegexFlag.IGNORECASE) @@ -1106,7 +1165,10 @@ def map_path_to_client(self, path: Union[os.PathLike[str], str]) -> pathlib.Pure def source_from_entry(self, entry: StackFrameEntry) -> Optional[Source]: if entry.source is not None and entry.is_file: - return Source(path=str(self.map_path_to_client(entry.source)), presentation_hint="normal") + return Source( + path=str(self.map_path_to_client(entry.source)), + presentation_hint="normal", + ) return None @@ -1171,7 +1233,13 @@ def log_message(self, message: Dict[str, Any]) -> None: RE_FILE_LINE_MATCHER = re.compile(r".+\sin\sfile\s'(?P.*)'\son\sline\s(?P\d+):.*") - def _send_log_event(self, timestamp: str, level: str, msg: str, category: Union[OutputCategory, str]) -> None: + def _send_log_event( + self, + timestamp: str, + level: str, + msg: str, + category: Union[OutputCategory, str], + ) -> None: current_frame = self.full_stack_frames[0] if self.full_stack_frames else None source = ( Source(path=str(self.map_path_to_client(current_frame.source))) @@ -1239,7 +1307,7 @@ def get_scopes(self, frame_id: int) -> List[Scope]: variables_reference=entry.local_id(), ) ) - if context.variables._test is not None and entry.type in ["KEYWORD"]: + if context.variables._test is not None and entry.type == "KEYWORD": result.append( Scope( name="Test", @@ -1248,7 +1316,10 @@ def get_scopes(self, frame_id: int) -> List[Scope]: variables_reference=entry.test_id(), ) ) - if context.variables._suite is not None and entry.type in ["TEST", "KEYWORD"]: + if context.variables._suite is not None and entry.type in [ + "TEST", + "KEYWORD", + ]: result.append( Scope( name="Suite", @@ -1389,7 +1460,8 @@ def get_variables( result[repr(i)] = self._create_variable(repr(k), v) if i >= 500: result["Unable to handle"] = self._create_variable( - "Unable to handle", "Maximum number of items (500) reached." + "Unable to handle", + "Maximum number of items (500) reached.", ) break @@ -1483,7 +1555,11 @@ def evaluate( if splitted: def run_kw() -> Any: - kw = Keyword(name=splitted[0], args=tuple(splitted[1:]), assign=tuple(variables)) + kw = Keyword( + name=splitted[0], + args=tuple(splitted[1:]), + assign=tuple(variables), + ) return kw.run(evaluate_context) result = self.run_in_robot_thread(run_kw) @@ -1659,7 +1735,11 @@ def _create_set_variable_result(self, value: Any) -> SetVariableResult: return SetVariableResult(value=repr(value), type=repr(type(value))) def set_variable( - self, variables_reference: int, name: str, value: str, format: Optional[ValueFormat] = None + self, + variables_reference: int, + name: str, + value: str, + format: Optional[ValueFormat] = None, ) -> SetVariableResult: entry = next( ( @@ -1697,7 +1777,12 @@ def set_exception_breakpoints( if filter_options is not None: for option in filter_options: - if option.filter_id in ["failed_keyword", "uncaught_failed_keyword", "failed_test", "failed_suite"]: + if option.filter_id in [ + "failed_keyword", + "uncaught_failed_keyword", + "failed_test", + "failed_suite", + ]: entry = ExceptionBreakpointsEntry( tuple(filters), tuple(filter_options) if filter_options is not None else None, @@ -1712,7 +1797,11 @@ def set_exception_breakpoints( return result or None def completions( - self, text: str, column: int, line: Optional[int] = None, frame_id: Optional[int] = None + self, + text: str, + column: int, + line: Optional[int] = None, + frame_id: Optional[int] = None, ) -> List[CompletionItem]: if self.expression_mode: return [] diff --git a/packages/debugger/src/robotcode/debugger/launcher/cli.py b/packages/debugger/src/robotcode/debugger/launcher/cli.py index c421f6d69..1988dbfe3 100644 --- a/packages/debugger/src/robotcode/debugger/launcher/cli.py +++ b/packages/debugger/src/robotcode/debugger/launcher/cli.py @@ -4,11 +4,11 @@ from robotcode.core.types import ServerMode from robotcode.core.utils.cli import show_hidden_arguments from robotcode.plugin import Application, UnknownError, pass_application -from robotcode.plugin.click_helper.options import resolve_server_options, server_options -from robotcode.plugin.click_helper.types import ( - AddressesPort, - add_options, +from robotcode.plugin.click_helper.options import ( + resolve_server_options, + server_options, ) +from robotcode.plugin.click_helper.types import AddressesPort, add_options from ..__version__ import __version__ from .run import run_launcher @@ -16,10 +16,7 @@ LAUNCHER_DEFAULT_PORT = 6611 -@click.command( - add_help_option=True, - hidden=show_hidden_arguments(), -) +@click.command(add_help_option=True, hidden=show_hidden_arguments()) @add_options(*server_options(ServerMode.STDIO, default_port=LAUNCHER_DEFAULT_PORT)) @click.version_option(version=__version__, prog_name="RobotCode Launcher") @pass_application @@ -40,7 +37,17 @@ def debug_launch( """Launches a robotcode debug session.""" mode, port, bind, pipe_name = resolve_server_options( - ctx, app, mode, port, bind, pipe_name, tcp, socket, stdio, pipe, pipe_server + ctx, + app, + mode, + port, + bind, + pipe_name, + tcp, + socket, + stdio, + pipe, + pipe_server, ) try: diff --git a/packages/debugger/src/robotcode/debugger/launcher/client.py b/packages/debugger/src/robotcode/debugger/launcher/client.py index 46167a1c3..3763f1be3 100644 --- a/packages/debugger/src/robotcode/debugger/launcher/client.py +++ b/packages/debugger/src/robotcode/debugger/launcher/client.py @@ -14,7 +14,7 @@ class DAPClientProtocol(DebugAdapterProtocol): _logger = LoggingDescriptor() - def __init__(self, parent: DebugAdapterProtocol, client: "DAPClient") -> None: + def __init__(self, parent: DebugAdapterProtocol, client: DAPClient) -> None: super().__init__() self.parent = parent self.client = client @@ -41,7 +41,11 @@ class DAPClientError(Exception): class DAPClient: _logger = LoggingDescriptor() - def __init__(self, parent: DebugAdapterProtocol, tcp_params: TcpParams = TcpParams(None, 0)) -> None: + def __init__( + self, + parent: DebugAdapterProtocol, + tcp_params: TcpParams = TcpParams(None, 0), + ) -> None: self.parent = parent self.tcp_params = tcp_params self._protocol: Optional[DAPClientProtocol] = None @@ -80,7 +84,10 @@ async def wait() -> None: host = self.tcp_params.host # type: ignore else: host = "127.0.0.1" - self._transport, protocol = await asyncio.get_running_loop().create_connection( + ( + self._transport, + protocol, + ) = await asyncio.get_running_loop().create_connection( self._create_protocol, host=host, port=self.tcp_params.port, diff --git a/packages/debugger/src/robotcode/debugger/launcher/server.py b/packages/debugger/src/robotcode/debugger/launcher/server.py index 1cc743e2f..40b68d838 100644 --- a/packages/debugger/src/robotcode/debugger/launcher/server.py +++ b/packages/debugger/src/robotcode/debugger/launcher/server.py @@ -197,13 +197,19 @@ async def _launch( robotcode_run_args += ["--tcp", str(port)] if debuggerTimeout is not None: - robotcode_run_args += ["--wait-for-client-timeout", str(debuggerTimeout)] + robotcode_run_args += [ + "--wait-for-client-timeout", + str(debuggerTimeout), + ] if attachPython and not no_debug: robotcode_run_args += ["--debugpy"] if attachPythonPort is not None and attachPythonPort != DEBUGPY_DEFAULT_PORT: - robotcode_run_args += ["--debugpy-port", str(attachPythonPort or 0)] + robotcode_run_args += [ + "--debugpy-port", + str(attachPythonPort or 0), + ] if outputMessages: robotcode_run_args += ["--output-messages"] @@ -287,15 +293,12 @@ async def _launch( ), return_type=RunInTerminalResponseBody, ) - elif console is None or console in ["internalConsole"]: + elif console is None or console == "internalConsole": run_env: Dict[str, Optional[str]] = dict(os.environ) run_env.update(env) await asyncio.get_event_loop().subprocess_exec( - lambda: OutputProtocol(self), - *run_args, - cwd=cwd, - env=run_env, + lambda: OutputProtocol(self), *run_args, cwd=cwd, env=run_env ) else: @@ -334,7 +337,12 @@ async def _configuration_done( await self.client.protocol.send_request_async(AttachRequest(arguments=AttachRequestArguments())) @rpc_method(name="disconnect", param_type=DisconnectArguments) - async def _disconnect(self, arguments: Optional[DisconnectArguments] = None, *args: Any, **kwargs: Any) -> None: + async def _disconnect( + self, + arguments: Optional[DisconnectArguments] = None, + *args: Any, + **kwargs: Any, + ) -> None: if self.connected: if not self.client.protocol.terminated: await self.client.protocol.send_request_async(DisconnectRequest(arguments=arguments)) @@ -344,7 +352,12 @@ async def _disconnect(self, arguments: Optional[DisconnectArguments] = None, *ar @_logger.call @rpc_method(name="terminate", param_type=TerminateArguments) - async def _terminate(self, arguments: Optional[TerminateArguments] = None, *args: Any, **kwargs: Any) -> None: + async def _terminate( + self, + arguments: Optional[TerminateArguments] = None, + *args: Any, + **kwargs: Any, + ) -> None: if self.client.connected: await self.client.protocol.send_request_async(TerminateRequest(arguments=arguments)) else: @@ -369,11 +382,7 @@ def __init__( pipe_name: Optional[str] = None, debugger_script: Optional[str] = None, ): - super().__init__( - mode=mode, - tcp_params=tcp_params, - pipe_name=pipe_name, - ) + super().__init__(mode=mode, tcp_params=tcp_params, pipe_name=pipe_name) self.debugger_script = debugger_script self.protocol: Optional[LauncherDebugAdapterProtocol] = None diff --git a/packages/debugger/src/robotcode/debugger/listeners.py b/packages/debugger/src/robotcode/debugger/listeners.py index f3d18780a..b4c8af87e 100644 --- a/packages/debugger/src/robotcode/debugger/listeners.py +++ b/packages/debugger/src/robotcode/debugger/listeners.py @@ -108,7 +108,7 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None: Debugger.instance().start_output_group( f"{name}({', '.join(repr(v) for v in attributes.get('args', []))})", attributes, - attributes.get("type", None), + attributes.get("type"), ) Debugger.instance().start_keyword(name, attributes) @@ -117,9 +117,9 @@ def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None: Debugger.instance().end_keyword(name, attributes) if attributes["type"] in ["KEYWORD", "SETUP", "TEARDOWN"]: - Debugger.instance().end_output_group(name, attributes, attributes.get("type", None)) + Debugger.instance().end_output_group(name, attributes, attributes.get("type")) - if attributes["status"] == "FAIL" and attributes.get("source", None): + if attributes["status"] == "FAIL" and attributes.get("source"): if self.failed_keywords is None: self.failed_keywords = [] @@ -262,7 +262,9 @@ def __init__(self) -> None: def start_suite(self, data: running.TestSuite, result: result.TestSuite) -> None: """Called when a suite starts.""" - def enqueue(item: Union[running.TestSuite, running.TestCase]) -> Iterator[str]: + def enqueue( + item: Union[running.TestSuite, running.TestCase], + ) -> Iterator[str]: if isinstance(item, running.TestSuite): yield f"{Path(item.source).resolve() if item.source is not None else ''};{item.longname}" @@ -279,13 +281,7 @@ def enqueue(item: Union[running.TestSuite, running.TestCase]) -> Iterator[str]: items = list(reversed(list(enqueue(cast(running.model.TestSuite, data))))) - Debugger.instance().send_event( - self, - Event( - event="robotEnqueued", - body={"items": items}, - ), - ) + Debugger.instance().send_event(self, Event(event="robotEnqueued", body={"items": items})) self._event_sended = True @@ -327,7 +323,10 @@ def report_status( p = next((i for i in data_item.tests if i.id == r.id), None) if data_item else None report_status(p, r, message) - if suite_data.teardown and suite_result.teardown.status in ["FAIL", "SKIP"]: + if suite_data.teardown and suite_result.teardown.status in [ + "FAIL", + "SKIP", + ]: report_status(suite_data, suite_result, message=suite_result.message) def start_test(self, data: running.TestCase, result: result.TestCase) -> None: diff --git a/packages/debugger/src/robotcode/debugger/protocol.py b/packages/debugger/src/robotcode/debugger/protocol.py index f43c06042..b0c62ba77 100644 --- a/packages/debugger/src/robotcode/debugger/protocol.py +++ b/packages/debugger/src/robotcode/debugger/protocol.py @@ -24,7 +24,11 @@ from robotcode.core.utils.dataclasses import as_dict, as_json, from_dict from robotcode.core.utils.inspect import ensure_coroutine from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.jsonrpc2.protocol import JsonRPCException, JsonRPCProtocolBase, SendedRequestEntry +from robotcode.jsonrpc2.protocol import ( + JsonRPCException, + JsonRPCProtocolBase, + SendedRequestEntry, +) from .dap_types import ( ErrorBody, @@ -38,13 +42,10 @@ class DebugAdapterErrorResponseError(JsonRPCException): - def __init__( - self, - error: ErrorResponse, - ) -> None: + def __init__(self, error: ErrorResponse) -> None: super().__init__( f'{error.message} (seq={error.request_seq} command="{error.command}")' - f'{f": {error.body.error}" if error.body is not None and error.body.error else ""}', + f'{f": {error.body.error}" if error.body is not None and error.body.error else ""}' ) self.error = error @@ -59,7 +60,7 @@ def __init__( error_message: Optional[Message] = None, ) -> None: super().__init__( - f'{(message+" ") if message else ""}(seq={request_seq} command="{command}")' + f'{(message + " ") if message else ""}(seq={request_seq} command="{command}")' f'{f": {error_message}" if error_message else ""}' ) self.message = message @@ -114,7 +115,7 @@ def send_error( @staticmethod def _generate_json_rpc_messages_from_dict( - data: Union[Dict[Any, Any], List[Dict[Any, Any]]] + data: Union[Dict[Any, Any], List[Dict[Any, Any]]], ) -> Iterator[ProtocolMessage]: def inner(d: Dict[Any, Any]) -> ProtocolMessage: result = from_dict(d, (Request, Response, Event)) @@ -163,7 +164,9 @@ async def handle_message(self, message: ProtocolMessage) -> None: @staticmethod def _convert_params( - callable: Callable[..., Any], param_type: Optional[Type[Any]], params: Any + callable: Callable[..., Any], + param_type: Optional[Type[Any]], + params: Any, ) -> Tuple[List[Any], Dict[str, Any]]: if params is None: return [], {} @@ -220,7 +223,9 @@ async def handle_unknown_command(self, message: Request) -> Any: raise DebugAdapterRPCErrorException( f"Unknown Command '{message.command}'", error_message=Message( - format='Unknown command "{command}"', variables={"command": str(message.command)}, show_user=True + format='Unknown command "{command}"', + variables={"command": str(message.command)}, + show_user=True, ), ) @@ -230,12 +235,16 @@ def handle_request(self, message: Request) -> None: with self._received_request_lock: if e is None or not callable(e.method): - result = asyncio.create_task(self.handle_unknown_command(message), name="handle_unknown_command") + result = asyncio.create_task( + self.handle_unknown_command(message), + name="handle_unknown_command", + ) else: params = self._convert_params(e.method, e.param_type, message.arguments) result = asyncio.create_task( - ensure_coroutine(e.method)(*params[0], **params[1]), name=e.method.__name__ + ensure_coroutine(e.method)(*params[0], **params[1]), + name=e.method.__name__, ) self._received_request[message.seq] = result @@ -289,7 +298,13 @@ def send_response( message: Optional[str] = None, ) -> None: self.send_message( - Response(request_seq=request_seq, command=command, success=success, message=message, body=result) + Response( + request_seq=request_seq, + command=command, + success=success, + message=message, + body=result, + ) ) @_logger.call @@ -336,7 +351,10 @@ def handle_response(self, message: Response) -> None: if entry is None: error = f"Invalid response. Could not find id '{message.request_seq}' in request list {message!r}" self._logger.warning(error) - self.send_error("invalid response", error_message=Message(format=error, show_user=True)) + self.send_error( + "invalid response", + error_message=Message(format=error, show_user=True), + ) return try: diff --git a/packages/debugger/src/robotcode/debugger/run.py b/packages/debugger/src/robotcode/debugger/run.py index 1dbfa5f28..d50a408a9 100644 --- a/packages/debugger/src/robotcode/debugger/run.py +++ b/packages/debugger/src/robotcode/debugger/run.py @@ -75,7 +75,9 @@ async def _debug_adapter_server_( from .server import DebugAdapterServer async with DebugAdapterServer( - mode=mode, tcp_params=TcpParams(addresses or "127.0.0.1", port), pipe_name=pipe_name + mode=mode, + tcp_params=TcpParams(addresses or "127.0.0.1", port), + pipe_name=pipe_name, ) as server: if on_config_done_callback is not None: server.protocol.received_configuration_done_callback = functools.partial(on_config_done_callback, server) @@ -105,7 +107,14 @@ async def start_debugpy_async( def connect_debugpy(server: "DebugAdapterServer") -> None: server.protocol.send_event( - Event(event="debugpyStarted", body={"port": port, "addresses": addresses, "processId": os.getpid()}) + Event( + event="debugpyStarted", + body={ + "port": port, + "addresses": addresses, + "processId": os.getpid(), + }, + ) ) if wait_for_debugpy_client: @@ -141,11 +150,21 @@ async def run_debugger( if debug and debugpy: app.verbose("Try to start debugpy session.") - await start_debugpy_async(debugpy_port, addresses, debugpy_wait_for_client, wait_for_client_timeout) + await start_debugpy_async( + debugpy_port, + addresses, + debugpy_wait_for_client, + wait_for_client_timeout, + ) app.verbose("Start robotcode debugger thread.") server_future = run_coroutine_in_thread( - _debug_adapter_server_, config_done_callback, mode, addresses, port, pipe_name + _debug_adapter_server_, + config_done_callback, + mode, + addresses, + port, + pipe_name, ) server = await wait_for_server() diff --git a/packages/debugger/src/robotcode/debugger/server.py b/packages/debugger/src/robotcode/debugger/server.py index 811190981..1ea7180c9 100644 --- a/packages/debugger/src/robotcode/debugger/server.py +++ b/packages/debugger/src/robotcode/debugger/server.py @@ -197,7 +197,10 @@ async def _attach( ) -> None: if pathMappings: Debugger.instance().path_mappings = [ - PathMapping(local_root=v.get("localRoot", None), remote_root=v.get("remoteRoot", None)) + PathMapping( + local_root=v.get("localRoot", None), + remote_root=v.get("remoteRoot", None), + ) for v in pathMappings ] Debugger.instance().attached = True @@ -220,7 +223,12 @@ async def terminate(self) -> None: self._terminated = True @rpc_method(name="terminate", param_type=TerminateArguments) - async def _terminate(self, arguments: Optional[TerminateArguments] = None, *args: Any, **kwargs: Any) -> None: + async def _terminate( + self, + arguments: Optional[TerminateArguments] = None, + *args: Any, + **kwargs: Any, + ) -> None: import signal if not self._sigint_signaled: @@ -237,7 +245,12 @@ async def _terminate(self, arguments: Optional[TerminateArguments] = None, *args signal.raise_signal(signal.SIGTERM) @rpc_method(name="disconnect", param_type=DisconnectArguments) - async def _disconnect(self, arguments: Optional[DisconnectArguments] = None, *args: Any, **kwargs: Any) -> None: + async def _disconnect( + self, + arguments: Optional[DisconnectArguments] = None, + *args: Any, + **kwargs: Any, + ) -> None: if ( (not (await self.exited) or not (await self.terminated)) and arguments is not None @@ -255,14 +268,20 @@ async def _set_breakpoints( ) -> SetBreakpointsResponseBody: return SetBreakpointsResponseBody( breakpoints=Debugger.instance().set_breakpoints( - arguments.source, arguments.breakpoints, arguments.lines, arguments.source_modified + arguments.source, + arguments.breakpoints, + arguments.lines, + arguments.source_modified, ) ) @_logger.call @rpc_method(name="configurationDone", param_type=ConfigurationDoneArguments) async def _configuration_done( - self, arguments: Optional[ConfigurationDoneArguments] = None, *args: Any, **kwargs: Any + self, + arguments: Optional[ConfigurationDoneArguments] = None, + *args: Any, + **kwargs: Any, ) -> None: self._received_configuration_done = True self._received_configuration_done_event.set() @@ -304,7 +323,10 @@ async def _threads(self, *args: Any, **kwargs: Any) -> ThreadsResponseBody: @rpc_method(name="stackTrace", param_type=StackTraceArguments) async def _stack_trace(self, arguments: StackTraceArguments, *args: Any, **kwargs: Any) -> StackTraceResponseBody: result = Debugger.instance().get_stack_trace( - arguments.thread_id, arguments.start_frame, arguments.levels, arguments.format + arguments.thread_id, + arguments.start_frame, + arguments.levels, + arguments.format, ) return StackTraceResponseBody(stack_frames=result.stack_frames, total_frames=result.total_frames) @@ -370,12 +392,20 @@ async def _set_variable( indexed_variables=result.indexed_variables, ) - @rpc_method(name="setExceptionBreakpoints", param_type=SetExceptionBreakpointsArguments) + @rpc_method( + name="setExceptionBreakpoints", + param_type=SetExceptionBreakpointsArguments, + ) async def _set_exception_breakpoints( - self, arguments: SetExceptionBreakpointsArguments, *args: Any, **kwargs: Any + self, + arguments: SetExceptionBreakpointsArguments, + *args: Any, + **kwargs: Any, ) -> Optional[SetExceptionBreakpointsResponseBody]: result = Debugger.instance().set_exception_breakpoints( - arguments.filters, arguments.filter_options, arguments.exception_options + arguments.filters, + arguments.filter_options, + arguments.exception_options, ) return SetExceptionBreakpointsResponseBody(breakpoints=result) if result else None @@ -401,11 +431,7 @@ def __init__( tcp_params: TcpParams = TcpParams(None, TCP_DEFAULT_PORT), pipe_name: Optional[str] = None, ): - super().__init__( - mode=mode, - tcp_params=tcp_params, - pipe_name=pipe_name, - ) + super().__init__(mode=mode, tcp_params=tcp_params, pipe_name=pipe_name) self.protocol = DebugAdapterServerProtocol() def create_protocol(self) -> DebugAdapterServerProtocol: diff --git a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py index b21f94796..974aacf29 100644 --- a/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py +++ b/packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py @@ -32,11 +32,12 @@ runtime_checkable, ) -from robotcode.core.async_tools import ( - create_sub_task, - run_coroutine_in_thread, +from robotcode.core.async_tools import create_sub_task, run_coroutine_in_thread +from robotcode.core.concurrent import ( + FutureEx, + is_threaded_callable, + run_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 @@ -176,7 +177,10 @@ def rpc_method(_func: _F) -> _F: @overload def rpc_method( - *, name: Optional[str] = None, param_type: Optional[Type[Any]] = None, cancelable: bool = True + *, + name: Optional[str] = None, + param_type: Optional[Type[Any]] = None, + cancelable: bool = True, ) -> Callable[[_F], _F]: ... @@ -229,7 +233,7 @@ def __init__(self, owner: Any = None) -> None: def __set_name__(self, owner: Any, name: str) -> None: self.__owner = owner - def __get__(self, obj: Any, obj_type: Type[Any]) -> "RpcRegistry": + def __get__(self, obj: Any, obj_type: Type[Any]) -> RpcRegistry: if obj is None and obj_type == self.__owner: return self @@ -315,7 +319,11 @@ def methods(self) -> Dict[str, RpcMethodEntry]: return self.__methods def add_method( - self, name: str, func: Callable[..., Any], param_type: Optional[Type[Any]] = None, cancelable: bool = True + self, + name: str, + func: Callable[..., Any], + param_type: Optional[Type[Any]] = None, + cancelable: bool = True, ) -> None: self.__ensure_initialized() @@ -349,7 +357,12 @@ def __init__(self, future: FutureEx[Any], result_type: Optional[Type[Any]]) -> N class ReceivedRequestEntry: - def __init__(self, future: asyncio.Future[Any], request: JsonRPCRequest, cancelable: bool) -> None: + def __init__( + self, + future: asyncio.Future[Any], + request: JsonRPCRequest, + cancelable: bool, + ) -> None: self.future = future self.request = request self.cancelable = cancelable @@ -367,7 +380,7 @@ class JsonRPCProtocolBase(asyncio.Protocol, ABC): def __init__(self) -> None: self.read_transport: Optional[asyncio.ReadTransport] = None self.write_transport: Optional[asyncio.WriteTransport] = None - self._message_buf = bytes() + self._message_buf = b"" self._loop: Optional[asyncio.AbstractEventLoop] = None @property @@ -430,7 +443,7 @@ def data_received(self, data: bytes) -> None: return body, data = body[:length], body[length:] - self._message_buf = bytes() + self._message_buf = b"" self._handle_body(body, charset) @@ -455,7 +468,7 @@ def __init__(self) -> None: @staticmethod def _generate_json_rpc_messages_from_dict( - data: Union[Dict[Any, Any], List[Dict[Any, Any]]] + data: Union[Dict[Any, Any], List[Dict[Any, Any]]], ) -> Iterator[JsonRPCMessage]: def inner(d: Dict[Any, Any]) -> JsonRPCMessage: if "jsonrpc" in d: @@ -527,12 +540,7 @@ def send_error( if data is not None: error_obj.data = data - self.send_message( - JsonRPCError( - id=id, - error=error_obj, - ) - ) + self.send_message(JsonRPCError(id=id, error=error_obj)) @__logger.call def send_message(self, message: JsonRPCMessage) -> None: @@ -633,7 +641,11 @@ async def handle_error(self, message: JsonRPCError) -> None: try: if not entry.future.done(): entry.future.set_exception( - JsonRPCErrorException(message.error.code, message.error.message, message.error.data) + JsonRPCErrorException( + message.error.code, + message.error.message, + message.error.data, + ) ) else: self.__logger.warning(lambda: f"Error Response for {message} is already done.") @@ -645,7 +657,10 @@ async def handle_error(self, message: JsonRPCError) -> None: entry.future.set_exception(e) def _convert_params( - self, 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 [], {} @@ -734,7 +749,9 @@ async def handle_request(self, message: JsonRPCRequest) -> None: if is_threaded_method: if e.is_coroutine: task = run_coroutine_in_thread( - ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1] + ensure_coroutine(cast(Callable[..., Any], e.method)), + *params[0], + **params[1], ) else: task = asyncio.wrap_future(run_in_thread(e.method, *params[0], **params[1])) @@ -766,25 +783,45 @@ def _received_request_done(self, message: JsonRPCRequest, t: asyncio.Future[Any] if entry.cancel_requested: self.__logger.debug(lambda: f"request {message!r} canceled") - self.send_error(JsonRPCErrors.REQUEST_CANCELLED, "Request canceled.", id=message.id) + self.send_error( + JsonRPCErrors.REQUEST_CANCELLED, + "Request canceled.", + id=message.id, + ) else: 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 + 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) + 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) + 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) + self.send_error( + JsonRPCErrors.INTERNAL_ERROR, + f"{type(e).__name__}: {e}", + id=message.id, + ) def cancel_request(self, id: Union[int, str, None]) -> None: with self._received_request_lock: @@ -813,7 +850,9 @@ async def handle_notification(self, message: JsonRPCNotification) -> None: else: if is_threaded_callable(e.method): task = run_coroutine_in_thread( - ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1] + ensure_coroutine(cast(Callable[..., Any], e.method)), + *params[0], + **params[1], ) else: task = create_sub_task( diff --git a/packages/jsonrpc2/src/robotcode/jsonrpc2/server.py b/packages/jsonrpc2/src/robotcode/jsonrpc2/server.py index 65dbe6edf..de3b51c93 100644 --- a/packages/jsonrpc2/src/robotcode/jsonrpc2/server.py +++ b/packages/jsonrpc2/src/robotcode/jsonrpc2/server.py @@ -225,13 +225,16 @@ def start_pipe(self, pipe_name: Optional[str]) -> None: # create_pipe_connection method if sys.platform == "win32" and hasattr(self.loop, "create_pipe_connection"): if TYPE_CHECKING: - from asyncio.streams import StreamReaderProtocol # noqa: F401 from asyncio.windows_events import ProactorEventLoop self.loop.run_until_complete( cast("ProactorEventLoop", self.loop).create_pipe_connection( - lambda: cast("StreamReaderProtocol", self.create_protocol()), pipe_name - ), + lambda: cast( + "asyncio.StreamReaderProtocol", + self.create_protocol(), + ), + pipe_name, + ) ) else: self.loop.run_until_complete(self.loop.create_unix_connection(self.create_protocol, pipe_name)) @@ -254,11 +257,11 @@ async def start_pipe_server(self, pipe_name: Optional[str]) -> None: # create_pipe_connection method if sys.platform == "win32" and hasattr(self.loop, "start_serving_pipe"): if TYPE_CHECKING: - from asyncio.streams import StreamReaderProtocol # noqa: F401 from asyncio.windows_events import ProactorEventLoop await cast("ProactorEventLoop", self.loop).start_serving_pipe( - lambda: cast("StreamReaderProtocol", self.create_protocol()), pipe_name + lambda: cast("asyncio.StreamReaderProtocol", self.create_protocol()), + pipe_name, ) else: diff --git a/packages/language_server/src/robotcode/language_server/cli.py b/packages/language_server/src/robotcode/language_server/cli.py index 63495f485..5168fdaf0 100644 --- a/packages/language_server/src/robotcode/language_server/cli.py +++ b/packages/language_server/src/robotcode/language_server/cli.py @@ -5,11 +5,11 @@ import click from robotcode.core.types import ServerMode, TcpParams from robotcode.plugin import Application, UnknownError, pass_application -from robotcode.plugin.click_helper.options import resolve_server_options, server_options -from robotcode.plugin.click_helper.types import ( - AddressesPort, - add_options, +from robotcode.plugin.click_helper.options import ( + resolve_server_options, + server_options, ) +from robotcode.plugin.click_helper.types import AddressesPort, add_options from robotcode.robot.config.loader import load_robot_config_from_path from robotcode.robot.config.model import RobotBaseProfile from robotcode.robot.config.utils import get_config_files @@ -29,14 +29,15 @@ def run_server( from .robotframework.server import RobotLanguageServer with RobotLanguageServer( - mode=mode, tcp_params=TcpParams(addresses or "127.0.0.1", port), pipe_name=pipe_name, profile=profile + mode=mode, + tcp_params=TcpParams(addresses or "127.0.0.1", port), + pipe_name=pipe_name, + profile=profile, ) as server: server.run() -@click.command( - add_help_option=True, -) +@click.command(add_help_option=True) @add_options( *server_options( ServerMode.STDIO, diff --git a/packages/language_server/src/robotcode/language_server/common/decorators.py b/packages/language_server/src/robotcode/language_server/common/decorators.py index 0b1e5ef78..7e36ec2ff 100644 --- a/packages/language_server/src/robotcode/language_server/common/decorators.py +++ b/packages/language_server/src/robotcode/language_server/common/decorators.py @@ -1,4 +1,12 @@ -from typing import Any, Callable, List, Protocol, TypeVar, Union, runtime_checkable +from typing import ( + Any, + Callable, + List, + Protocol, + TypeVar, + Union, + runtime_checkable, +) from robotcode.core.lsp.types import CodeActionKind @@ -68,7 +76,9 @@ class HasAllCommitCharacters(Protocol): CODE_ACTION_KINDS_ATTR = "__code_action_kinds__" -def code_action_kinds(kinds: List[Union[CodeActionKind, str]]) -> Callable[[_F], _F]: +def code_action_kinds( + kinds: List[Union[CodeActionKind, str]], +) -> Callable[[_F], _F]: def decorator(func: _F) -> _F: setattr(func, CODE_ACTION_KINDS_ATTR, kinds) return func @@ -81,7 +91,9 @@ class HasCodeActionKinds(Protocol): __code_action_kinds__: List[str] -def language_id_filter(language_id_or_document: Union[str, TextDocument]) -> Callable[[Any], bool]: +def language_id_filter( + language_id_or_document: Union[str, TextDocument], +) -> Callable[[Any], bool]: def filter(c: Any) -> bool: return not hasattr(c, LANGUAGE_ID_ATTR) or ( ( diff --git a/packages/language_server/src/robotcode/language_server/common/parts/code_action.py b/packages/language_server/src/robotcode/language_server/common/parts/code_action.py index a57dafb91..8fcb851e8 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/code_action.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/code_action.py @@ -16,8 +16,14 @@ ) from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method -from robotcode.language_server.common.decorators import CODE_ACTION_KINDS_ATTR, HasCodeActionKinds, language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.decorators import ( + CODE_ACTION_KINDS_ATTR, + HasCodeActionKinds, + language_id_filter, +) +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -32,7 +38,10 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, range: Range, context: CodeActionContext # NOSONAR + sender, + document: TextDocument, + range: Range, + context: CodeActionContext, # NOSONAR ) -> Optional[List[Union[Command, CodeAction]]]: ... @@ -80,7 +89,11 @@ def _text_document_code_action( r.location.range = document.range_from_utf16(r.location.range) for result in self.collect( - self, document, document.range_from_utf16(range), context, callback_filter=language_id_filter(document) + self, + document, + document.range_from_utf16(range), + context, + callback_filter=language_id_filter(document), ): check_current_thread_canceled() @@ -98,12 +111,7 @@ def _text_document_code_action( @rpc_method(name="codeAction/resolve", param_type=CodeAction) @threaded - def _text_document_code_action_resolve( - self, - params: CodeAction, - *args: Any, - **kwargs: Any, - ) -> CodeAction: + def _text_document_code_action_resolve(self, params: CodeAction, *args: Any, **kwargs: Any) -> CodeAction: results: List[CodeAction] = [] for result in self.resolve(self, params): 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 3eafdd09d..dbf60a300 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,12 @@ from concurrent.futures import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, 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, diff --git a/packages/language_server/src/robotcode/language_server/common/parts/commands.py b/packages/language_server/src/robotcode/language_server/common/parts/commands.py index 851a605c5..602ffc2bb 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/commands.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/commands.py @@ -2,7 +2,16 @@ import typing import uuid from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable, Dict, Final, List, Optional, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Final, + List, + Optional, + cast, +) from robotcode.core.concurrent import threaded from robotcode.core.lsp.types import ( @@ -15,8 +24,13 @@ from robotcode.core.utils.dataclasses import from_dict from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import JsonRPCErrorException, rpc_method -from robotcode.language_server.common.decorators import get_command_id, is_command -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.decorators import ( + get_command_id, + is_command, +) +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) if TYPE_CHECKING: from robotcode.language_server.common.protocol import LanguageServerProtocol @@ -71,7 +85,11 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @rpc_method(name="workspace/executeCommand", param_type=ExecuteCommandParams) @threaded def _workspace_execute_command( - self, command: str, arguments: Optional[List[LSPAny]], *args: Any, **kwargs: Any + self, + command: str, + arguments: Optional[List[LSPAny]], + *args: Any, + **kwargs: Any, ) -> Optional[LSPAny]: self._logger.debug(lambda: f"execute command {command}") diff --git a/packages/language_server/src/robotcode/language_server/common/parts/completion.py b/packages/language_server/src/robotcode/language_server/common/parts/completion.py index a92a5965f..e79768b96 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/completion.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/completion.py @@ -27,7 +27,9 @@ HasTriggerCharacters, language_id_filter, ) -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -42,7 +44,10 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, position: Position, context: Optional[CompletionContext] # NOSONAR + sender, + document: TextDocument, + position: Position, + context: Optional[CompletionContext], # NOSONAR ) -> Union[List[CompletionItem], CompletionList, None]: ... @@ -100,7 +105,13 @@ def _text_document_completion( p = document.position_from_utf16(position) - for result in self.collect(self, document, p, context, callback_filter=language_id_filter(document)): + for result in self.collect( + self, + document, + p, + context, + callback_filter=language_id_filter(document), + ): check_current_thread_canceled() if isinstance(result, BaseException): @@ -150,12 +161,7 @@ def update_completion_item_to_utf16(self, document: TextDocument, item: Completi @rpc_method(name="completionItem/resolve", param_type=CompletionItem) @threaded - def _completion_item_resolve( - self, - params: CompletionItem, - *args: Any, - **kwargs: Any, - ) -> CompletionItem: + def _completion_item_resolve(self, params: CompletionItem, *args: Any, **kwargs: Any) -> CompletionItem: results: List[CompletionItem] = [] for result in self.resolve(self, params): diff --git a/packages/language_server/src/robotcode/language_server/common/parts/declaration.py b/packages/language_server/src/robotcode/language_server/common/parts/declaration.py index c1e56f8bc..43283ea2c 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/declaration.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/declaration.py @@ -14,7 +14,9 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -30,7 +32,9 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, position: Position # NOSONAR + sender, + document: TextDocument, + position: Position, # NOSONAR ) -> Union[Location, List[Location], List[LocationLink], None]: ... @@ -61,7 +65,12 @@ def _text_document_declaration( if document is None: return None - for result in self.collect(self, document, position, callback_filter=language_id_filter(document)): + for result in self.collect( + self, + document, + position, + callback_filter=language_id_filter(document), + ): check_current_thread_canceled() if isinstance(result, BaseException): diff --git a/packages/language_server/src/robotcode/language_server/common/parts/definition.py b/packages/language_server/src/robotcode/language_server/common/parts/definition.py index 8bf8e8565..d52895e28 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/definition.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/definition.py @@ -14,7 +14,9 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -30,7 +32,9 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, position: Position # NOSONAR + sender, + document: TextDocument, + position: Position, # NOSONAR ) -> Union[Location, List[Location], List[LocationLink], None]: ... @@ -48,7 +52,11 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @rpc_method(name="textDocument/definition", param_type=DefinitionParams) @threaded def _text_document_definition( - self, text_document: TextDocumentIdentifier, position: Position, *args: Any, **kwargs: Any + self, + text_document: TextDocumentIdentifier, + position: Position, + *args: Any, + **kwargs: Any, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: locations: List[Location] = [] location_links: List[LocationLink] = [] @@ -58,7 +66,10 @@ def _text_document_definition( return None for result in self.collect( - self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document) + self, + document, + document.position_from_utf16(position), + callback_filter=language_id_filter(document), ): check_current_thread_canceled() 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 5725e852d..52fbcf7b4 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 @@ -7,7 +7,12 @@ from threading import Event, RLock from typing import TYPE_CHECKING, Any, Dict, Final, List, Optional, cast -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, 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 ( Diagnostic, @@ -29,7 +34,9 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import JsonRPCErrorException, rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -129,7 +136,9 @@ def collect(sender, document: TextDocument) -> Optional[DiagnosticsResult]: # N ... @event - def load_workspace_documents(sender) -> Optional[List[WorkspaceDocumentsResult]]: # NOSONAR + def load_workspace_documents( + sender, + ) -> Optional[List[WorkspaceDocumentsResult]]: # NOSONAR ... @event @@ -220,7 +229,11 @@ def run_workspace_diagnostics(self) -> None: start = time.monotonic() with self.parent.window.progress( - "Analyse workspace", cancellable=False, current=0, max=len(documents) + 1, start=False + "Analyse workspace", + cancellable=False, + current=0, + max=len(documents) + 1, + start=False, ) as progress: for i, document in enumerate(documents): check_current_thread_canceled() @@ -267,10 +280,17 @@ def run_workspace_diagnostics(self) -> None: raise except BaseException as e: ex = e - self._logger.exception(lambda: f"Error in workspace diagnostics loop: {ex}", exc_info=e) + self._logger.exception( + lambda: f"Error in workspace diagnostics loop: {ex}", + exc_info=e, + ) def create_document_diagnostics_task( - self, document: TextDocument, single: bool, debounce: bool = True, send_diagnostics: bool = True + self, + document: TextDocument, + single: bool, + debounce: bool = True, + send_diagnostics: bool = True, ) -> FutureEx[Any]: def done(t: FutureEx[Any]) -> None: self._logger.debug(lambda: f"diagnostics for {document} {'canceled' if t.cancelled() else 'ended'}") @@ -288,7 +308,13 @@ def done(t: FutureEx[Any]) -> None: future.cancel() data.version = document.version - data.future = run_in_thread(self._get_diagnostics_for_document, document, data, debounce, send_diagnostics) + data.future = run_in_thread( + self._get_diagnostics_for_document, + document, + data, + debounce, + send_diagnostics, + ) data.future.add_done_callback(done) @@ -296,7 +322,11 @@ def done(t: FutureEx[Any]) -> None: @_logger.call def _get_diagnostics_for_document( - self, document: TextDocument, data: DiagnosticsData, debounce: bool = True, send_diagnostics: bool = True + self, + document: TextDocument, + data: DiagnosticsData, + debounce: bool = True, + send_diagnostics: bool = True, ) -> None: self._logger.debug(lambda: f"Get diagnostics for {document}") @@ -306,7 +336,10 @@ def _get_diagnostics_for_document( collected_keys: List[Any] = [] try: for result in self.collect( - self, document, callback_filter=language_id_filter(document), return_exceptions=True + self, + document, + callback_filter=language_id_filter(document), + return_exceptions=True, ): check_current_thread_canceled() @@ -370,12 +403,17 @@ def _text_document_diagnostic( try: if not self.parent.is_initialized.is_set(): raise JsonRPCErrorException( - LSPErrorCodes.SERVER_CANCELLED, "Server not initialized.", DiagnosticServerCancellationData(True) + LSPErrorCodes.SERVER_CANCELLED, + "Server not initialized.", + DiagnosticServerCancellationData(True), ) document = self.parent.documents.get(text_document.uri) if document is None: - raise JsonRPCErrorException(LSPErrorCodes.SERVER_CANCELLED, f"Document {text_document!r} not found.") + raise JsonRPCErrorException( + LSPErrorCodes.SERVER_CANCELLED, + f"Document {text_document!r} not found.", + ) self.create_document_diagnostics_task(document, True) diff --git a/packages/language_server/src/robotcode/language_server/common/parts/document_highlight.py b/packages/language_server/src/robotcode/language_server/common/parts/document_highlight.py index 1dc86ddb6..cabc090b5 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/document_highlight.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/document_highlight.py @@ -14,7 +14,9 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -35,7 +37,10 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: def collect(sender, document: TextDocument, position: Position) -> Optional[List[DocumentHighlight]]: # NOSONAR ... - @rpc_method(name="textDocument/documentHighlight", param_type=DocumentHighlightParams) + @rpc_method( + name="textDocument/documentHighlight", + param_type=DocumentHighlightParams, + ) @threaded def _text_document_document_highlight( self, @@ -51,7 +56,10 @@ def _text_document_document_highlight( return None for result in self.collect( - self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document) + self, + document, + document.position_from_utf16(position), + callback_filter=language_id_filter(document), ): if isinstance(result, BaseException): if not isinstance(result, CancelledError): diff --git a/packages/language_server/src/robotcode/language_server/common/parts/document_symbols.py b/packages/language_server/src/robotcode/language_server/common/parts/document_symbols.py index 76091f2e9..fc74b472d 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/document_symbols.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/document_symbols.py @@ -29,7 +29,9 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: @@ -63,7 +65,8 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument # NOSONAR + sender, + document: TextDocument, # NOSONAR ) -> Optional[Union[List[DocumentSymbol], List[SymbolInformation], None]]: ... 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 5d780c8cb..99e84297c 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 @@ -101,17 +101,29 @@ def _file_watcher(self, sender: Any, changes: List[FileEvent]) -> None: elif document is not None and not document.opened_in_editor: if change.type == FileChangeType.DELETED: self.close_document(document, True) - self.did_close(self, document, callback_filter=language_id_filter(document)) + self.did_close( + self, + document, + callback_filter=language_id_filter(document), + ) elif change.type == FileChangeType.CHANGED: document.apply_full_change( - None, self.read_document_text(document.uri, language_id_filter(document)), save=True + None, + self.read_document_text(document.uri, language_id_filter(document)), + save=True, + ) + self.did_change( + self, + document, + callback_filter=language_id_filter(document), ) - self.did_change(self, document, callback_filter=language_id_filter(document)) def read_document_text(self, uri: Uri, language_id: Union[str, Callable[[Any], bool], None]) -> str: 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 + 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 @@ -132,7 +144,10 @@ def detect_language_id(self, path_or_uri: Union[str, os.PathLike[Any], Uri]) -> @_logger.call def get_or_open_document( - self, path: Union[str, os.PathLike[Any]], language_id: Optional[str] = None, version: Optional[int] = None + self, + path: Union[str, os.PathLike[Any]], + language_id: Optional[str] = None, + version: Optional[int] = None, ) -> TextDocument: uri = Uri.from_path(path).normalized() @@ -182,7 +197,10 @@ def did_save(sender, document: TextDocument) -> None: # NOSONAR def get(self, _uri: Union[DocumentUri, Uri]) -> Optional[TextDocument]: with self._lock: - return self._documents.get(str(Uri(_uri).normalized() if not isinstance(_uri, Uri) else _uri), None) + return self._documents.get( + str(Uri(_uri).normalized() if not isinstance(_uri, Uri) else _uri), + None, + ) def __len__(self) -> int: return self._documents.__len__() @@ -216,7 +234,10 @@ def append_document( ) -> TextDocument: with self._lock: document = self._create_document( - document_uri=document_uri, language_id=language_id, text=text, version=version + document_uri=document_uri, + language_id=language_id, + text=text, + version=version, ) self._documents[document_uri] = document @@ -294,7 +315,11 @@ def close_document(self, document: TextDocument, real_close: bool = False) -> No @rpc_method(name="textDocument/willSave", param_type=WillSaveTextDocumentParams) @_logger.call def _text_document_will_save( - self, text_document: TextDocumentIdentifier, reason: TextDocumentSaveReason, *args: Any, **kwargs: Any + self, + text_document: TextDocumentIdentifier, + reason: TextDocumentSaveReason, + *args: Any, + **kwargs: Any, ) -> None: # TODO: implement pass @@ -302,10 +327,17 @@ def _text_document_will_save( @rpc_method(name="textDocument/didSave", param_type=DidSaveTextDocumentParams) @_logger.call def _text_document_did_save( - self, text_document: TextDocumentIdentifier, text: Optional[str] = None, *args: Any, **kwargs: Any + self, + text_document: TextDocumentIdentifier, + text: Optional[str] = None, + *args: Any, + **kwargs: Any, ) -> None: document = self.get(str(Uri(text_document.uri).normalized())) - self._logger.warning(lambda: f"Document {text_document.uri} is not opened.", condition=lambda: document is None) + self._logger.warning( + lambda: f"Document {text_document.uri} is not opened.", + condition=lambda: document is None, + ) if document is not None: if text is not None: @@ -314,14 +346,25 @@ def _text_document_did_save( text_changed = document.text() != normalized_text if text_changed: document.save(None, text) - self.did_change(self, document, callback_filter=language_id_filter(document)) + self.did_change( + self, + document, + callback_filter=language_id_filter(document), + ) self.did_save(self, document, callback_filter=language_id_filter(document)) - @rpc_method(name="textDocument/willSaveWaitUntil", param_type=WillSaveTextDocumentParams) + @rpc_method( + name="textDocument/willSaveWaitUntil", + param_type=WillSaveTextDocumentParams, + ) @_logger.call def _text_document_will_save_wait_until( - self, text_document: TextDocumentIdentifier, reason: TextDocumentSaveReason, *args: Any, **kwargs: Any + self, + text_document: TextDocumentIdentifier, + reason: TextDocumentSaveReason, + *args: Any, + **kwargs: Any, ) -> List[TextEdit]: return [] @@ -340,9 +383,15 @@ def _text_document_did_change( sync_kind = ( self.parent.capabilities.text_document_sync - if isinstance(self.parent.capabilities.text_document_sync, TextDocumentSyncKind) + if isinstance( + self.parent.capabilities.text_document_sync, + TextDocumentSyncKind, + ) else self.parent.capabilities.text_document_sync.change - if isinstance(self.parent.capabilities.text_document_sync, TextDocumentSyncOptions) + if isinstance( + self.parent.capabilities.text_document_sync, + TextDocumentSyncOptions, + ) else None ) for content_change in content_changes: @@ -351,12 +400,17 @@ def _text_document_did_change( elif sync_kind == TextDocumentSyncKind.FULL and isinstance( content_change, TextDocumentContentChangeEventType2 ): - document.apply_full_change(text_document.version, self._normalize_line_endings(content_change.text)) + document.apply_full_change( + text_document.version, + self._normalize_line_endings(content_change.text), + ) elif sync_kind == TextDocumentSyncKind.INCREMENTAL and isinstance( content_change, TextDocumentContentChangeEventType1 ): document.apply_incremental_change( - text_document.version, content_change.range, self._normalize_line_endings(content_change.text) + text_document.version, + content_change.range, + self._normalize_line_endings(content_change.text), ) else: raise LanguageServerDocumentError( diff --git a/packages/language_server/src/robotcode/language_server/common/parts/folding_range.py b/packages/language_server/src/robotcode/language_server/common/parts/folding_range.py index 6532321a6..3edcf4637 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/folding_range.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/folding_range.py @@ -13,11 +13,15 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method from robotcode.language_server.common.decorators import language_id_filter -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: - from robotcode.language_server.common.protocol import LanguageServerProtocol # pragma: no cover + from robotcode.language_server.common.protocol import ( + LanguageServerProtocol, + ) class FoldingRangeProtocolPart(LanguageServerProtocolPart): diff --git a/packages/language_server/src/robotcode/language_server/common/parts/formatting.py b/packages/language_server/src/robotcode/language_server/common/parts/formatting.py index 77c9a7a13..e86a2543a 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/formatting.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/formatting.py @@ -96,7 +96,10 @@ def _text_document_formatting( return None - @rpc_method(name="textDocument/rangeFormatting", param_type=DocumentRangeFormattingParams) + @rpc_method( + name="textDocument/rangeFormatting", + param_type=DocumentRangeFormattingParams, + ) @threaded def _text_document_range_formatting( self, diff --git a/packages/language_server/src/robotcode/language_server/common/parts/implementation.py b/packages/language_server/src/robotcode/language_server/common/parts/implementation.py index d81a2d5ff..b1cb72a16 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/implementation.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/implementation.py @@ -31,7 +31,9 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, position: Position # NOSONAR + sender, + document: TextDocument, + position: Position, # NOSONAR ) -> Union[Location, List[Location], List[LocationLink], None]: ... @@ -49,7 +51,11 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @rpc_method(name="textDocument/implementation", param_type=ImplementationParams) @threaded def _text_document_implementation( - self, text_document: TextDocumentIdentifier, position: Position, *args: Any, **kwargs: Any + self, + text_document: TextDocumentIdentifier, + position: Position, + *args: Any, + **kwargs: Any, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: locations: List[Location] = [] location_links: List[LocationLink] = [] 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 d2f3fe87b..a13e812f1 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,12 @@ from concurrent.futures import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, 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, @@ -84,12 +89,7 @@ def _text_document_inlay_hint( @rpc_method(name="inlayHint/resolve", param_type=InlayHint) @threaded - def _inlay_hint_resolve( - self, - params: InlayHint, - *args: Any, - **kwargs: Any, - ) -> Optional[InlayHint]: + def _inlay_hint_resolve(self, params: InlayHint, *args: Any, **kwargs: Any) -> Optional[InlayHint]: for result in self.resolve(self, params): if isinstance(result, BaseException): if not isinstance(result, CancelledError): 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 d7579acf7..1284ace06 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,12 @@ from asyncio import CancelledError from typing import TYPE_CHECKING, Any, Final, List, Optional -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, 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, @@ -16,14 +21,19 @@ ) from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method -from robotcode.language_server.common.decorators import LANGUAGE_ID_ATTR, language_id_filter +from robotcode.language_server.common.decorators import ( + LANGUAGE_ID_ATTR, + language_id_filter, +) from robotcode.language_server.common.parts.protocol_part import ( LanguageServerProtocolPart, ) from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: - from robotcode.language_server.common.protocol import LanguageServerProtocol # pragma: no cover + from robotcode.language_server.common.protocol import ( + LanguageServerProtocol, + ) class InlineValueProtocolPart(LanguageServerProtocolPart): @@ -71,7 +81,11 @@ def _text_document_inline_value( return None for result in self.collect( - self, document, document.range_from_utf16(range), context, callback_filter=language_id_filter(document) + self, + document, + document.range_from_utf16(range), + context, + callback_filter=language_id_filter(document), ): check_current_thread_canceled() diff --git a/packages/language_server/src/robotcode/language_server/common/parts/linked_editing_ranges.py b/packages/language_server/src/robotcode/language_server/common/parts/linked_editing_ranges.py index 927a39a36..c776ed1f8 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/linked_editing_ranges.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/linked_editing_ranges.py @@ -37,7 +37,10 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: def collect(sender, document: TextDocument, position: Position) -> Optional[LinkedEditingRanges]: # NOSONAR ... - @rpc_method(name="textDocument/linkedEditingRange", param_type=LinkedEditingRangeParams) + @rpc_method( + name="textDocument/linkedEditingRange", + param_type=LinkedEditingRangeParams, + ) @threaded def _text_document_linked_editing_range( self, @@ -54,7 +57,10 @@ def _text_document_linked_editing_range( return None for result in self.collect( - self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document) + self, + document, + document.position_from_utf16(position), + callback_filter=language_id_filter(document), ): check_current_thread_canceled() diff --git a/packages/language_server/src/robotcode/language_server/common/parts/references.py b/packages/language_server/src/robotcode/language_server/common/parts/references.py index 2b3efa1d2..201557466 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/references.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/references.py @@ -35,7 +35,10 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @event def collect( - sender, document: TextDocument, position: Position, context: ReferenceContext # NOSONAR + sender, + document: TextDocument, + position: Position, + context: ReferenceContext, # NOSONAR ) -> Optional[List[Location]]: ... diff --git a/packages/language_server/src/robotcode/language_server/common/parts/rename.py b/packages/language_server/src/robotcode/language_server/common/parts/rename.py index 5353bfb0e..f8db8b3f6 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/rename.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/rename.py @@ -41,12 +41,16 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: def extend_capabilities(self, capabilities: ServerCapabilities) -> None: if len(self.collect): capabilities.rename_provider = RenameOptions( - prepare_provider=len(self.collect_prepare) > 0, work_done_progress=True + prepare_provider=len(self.collect_prepare) > 0, + work_done_progress=True, ) @event def collect( - sender, document: TextDocument, position: Position, new_name: str # NOSONAR + sender, + document: TextDocument, + position: Position, + new_name: str, # NOSONAR ) -> Optional[WorkspaceEdit]: ... @@ -143,7 +147,10 @@ def _text_document_prepare_rename( return None for result in self.collect_prepare( - self, document, document.position_from_utf16(position), callback_filter=language_id_filter(document) + self, + document, + document.position_from_utf16(position), + callback_filter=language_id_filter(document), ): check_current_thread_canceled() 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 f6413a2a2..dbe8c5043 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 @@ -2,7 +2,12 @@ from enum import Enum from typing import TYPE_CHECKING, Any, Final, List, Optional, Union -from robotcode.core.concurrent import FutureEx, check_current_thread_canceled, run_in_thread, 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, @@ -45,7 +50,9 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect_full( - sender, document: TextDocument, **kwargs: Any # NOSONAR + sender, + document: TextDocument, + **kwargs: Any, # NOSONAR ) -> Union[SemanticTokens, SemanticTokensPartialResult, None]: ... @@ -55,12 +62,15 @@ def collect_full_delta( document: TextDocument, previous_result_id: str, **kwargs: Any, # NOSONAR - ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None]: + ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None,]: ... @event def collect_range( - sender, document: TextDocument, range: Range, **kwargs: Any # NOSONAR + sender, + document: TextDocument, + range: Range, + **kwargs: Any, # NOSONAR ) -> Union[SemanticTokens, SemanticTokensPartialResult, None]: ... @@ -90,7 +100,12 @@ def _text_document_semantic_tokens_full( if document is None: return None - for result in self.collect_full(self, document, callback_filter=language_id_filter(document), **kwargs): + for result in self.collect_full( + self, + document, + callback_filter=language_id_filter(document), + **kwargs, + ): check_current_thread_canceled() if isinstance(result, BaseException): @@ -117,15 +132,25 @@ def _text_document_semantic_tokens_full_delta( previous_result_id: str, *args: Any, **kwargs: Any, - ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None]: - results: List[Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult]] = [] + ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None,]: + results: List[ + Union[ + SemanticTokens, + SemanticTokensDelta, + SemanticTokensDeltaPartialResult, + ] + ] = [] document = self.parent.documents.get(text_document.uri) if document is None: return None for result in self.collect_full_delta( - self, document, previous_result_id, callback_filter=language_id_filter(document), **kwargs + self, + document, + previous_result_id, + callback_filter=language_id_filter(document), + **kwargs, ): check_current_thread_canceled() if isinstance(result, BaseException): @@ -141,7 +166,10 @@ def _text_document_semantic_tokens_full_delta( return None - @rpc_method(name="textDocument/semanticTokens/range", param_type=SemanticTokensRangeParams) + @rpc_method( + name="textDocument/semanticTokens/range", + param_type=SemanticTokensRangeParams, + ) @threaded def _text_document_semantic_tokens_range( self, @@ -156,7 +184,13 @@ def _text_document_semantic_tokens_range( if document is None: return None - for result in self.collect_range(self, document, range, callback_filter=language_id_filter(document), **kwargs): + for result in self.collect_range( + self, + document, + range, + callback_filter=language_id_filter(document), + **kwargs, + ): check_current_thread_canceled() if isinstance(result, BaseException): if not isinstance(result, CancelledError): diff --git a/packages/language_server/src/robotcode/language_server/common/parts/signature_help.py b/packages/language_server/src/robotcode/language_server/common/parts/signature_help.py index e0fc1266a..e1cf1c1ee 100644 --- a/packages/language_server/src/robotcode/language_server/common/parts/signature_help.py +++ b/packages/language_server/src/robotcode/language_server/common/parts/signature_help.py @@ -38,7 +38,10 @@ def __init__(self, parent: "LanguageServerProtocol") -> None: @event def collect( - sender, document: TextDocument, position: Position, context: Optional[SignatureHelpContext] = None # NOSONAR + sender, + document: TextDocument, + position: Position, + context: Optional[SignatureHelpContext] = None, # NOSONAR ) -> Optional[SignatureHelp]: ... 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 358860651..a93c7c46f 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 @@ -147,7 +147,12 @@ def show_document( r = self.parent.send_request( "window/showDocument", - ShowDocumentParams(uri=uri, external=external, take_focus=take_focus, selection=selection), + ShowDocumentParams( + uri=uri, + external=external, + take_focus=take_focus, + selection=selection, + ), ShowDocumentResult, ).result(30) return r.success if r is not None else False @@ -192,19 +197,20 @@ 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)) + self.parent.send_request( + "window/workDoneProgress/create", + WorkDoneProgressCreateParams(token), + ) self.__progress_tokens[token] = False return token return None - @rpc_method(name="window/workDoneProgress/cancel", param_type=WorkDoneProgressCancelParams) - def _window_work_done_progress_cancel( - self, - token: ProgressToken, - *args: Any, - **kwargs: Any, - ) -> None: + @rpc_method( + name="window/workDoneProgress/cancel", + param_type=WorkDoneProgressCancelParams, + ) + def _window_work_done_progress_cancel(self, token: ProgressToken, *args: Any, **kwargs: Any) -> None: if token in self.__progress_tokens: self.__progress_tokens[token] = True @@ -272,11 +278,7 @@ def progress_report( ), ) - def progress_end( - self, - token: Optional[ProgressToken], - message: Optional[str] = None, - ) -> None: + def progress_end(self, token: Optional[ProgressToken], message: Optional[str] = None) -> None: if ( token is not None and self.parent.client_capabilities 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 da805609d..60efde5bb 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 @@ -1,7 +1,6 @@ import threading import uuid import weakref -from concurrent.futures import Future from typing import ( TYPE_CHECKING, Any, @@ -21,7 +20,7 @@ cast, ) -from robotcode.core.concurrent import threaded +from robotcode.core.concurrent import FutureEx, threaded from robotcode.core.event import event from robotcode.core.lsp.types import ( ApplyWorkspaceEditParams, @@ -54,9 +53,7 @@ WorkspaceFoldersChangeEvent, WorkspaceFoldersServerCapabilities, ) -from robotcode.core.lsp.types import ( - WorkspaceFolder as TypesWorkspaceFolder, -) +from robotcode.core.lsp.types import WorkspaceFolder as TypesWorkspaceFolder from robotcode.core.uri import Uri from robotcode.core.utils.dataclasses import CamelSnakeMixin, from_dict from robotcode.core.utils.logging import LoggingDescriptor @@ -249,7 +246,10 @@ def settings(self, value: Dict[str, Any]) -> None: def did_change_configuration(sender, settings: Dict[str, Any]) -> None: # NOSONAR ... - @rpc_method(name="workspace/didChangeConfiguration", param_type=DidChangeConfigurationParams) + @rpc_method( + name="workspace/didChangeConfiguration", + param_type=DidChangeConfigurationParams, + ) @threaded def _workspace_did_change_configuration(self, settings: Dict[str, Any], *args: Any, **kwargs: Any) -> None: self.settings = settings @@ -340,16 +340,21 @@ def get_configuration_future( section: Type[_TConfig], scope_uri: Union[str, Uri, None] = None, request: bool = True, - ) -> Future[_TConfig]: - result_future: Future[_TConfig] = Future() + ) -> FutureEx[_TConfig]: + result_future: FutureEx[_TConfig] = FutureEx() scope = self.get_workspace_folder(scope_uri) if scope_uri is not None else None if (scope, section.__config_section__) in self._settings_cache: - result_future.set_result(cast(_TConfig, self._settings_cache[(scope, section.__config_section__)])) + result_future.set_result( + cast( + _TConfig, + self._settings_cache[(scope, section.__config_section__)], + ) + ) return result_future - def _get_configuration_done(f: Future[Optional[Any]]) -> None: + def _get_configuration_done(f: FutureEx[Optional[Any]]) -> None: try: if result_future.cancelled(): return @@ -382,7 +387,7 @@ def get_configuration_raw( section: Optional[str], scope_uri: Union[str, Uri, None] = None, request: bool = True, - ) -> Future[Optional[Any]]: + ) -> FutureEx[Optional[Any]]: if ( self.parent.client_capabilities and self.parent.client_capabilities.workspace @@ -409,7 +414,7 @@ def get_configuration_raw( else: result = {} break - result_future: Future[Optional[Any]] = Future() + result_future: FutureEx[Optional[Any]] = FutureEx() result_future.set_result([result]) return result_future @@ -458,7 +463,10 @@ def _workspace_did_change_workspace_folders( def did_change_watched_files(sender, changes: List[FileEvent]) -> None: # NOSONAR ... - @rpc_method(name="workspace/didChangeWatchedFiles", param_type=DidChangeWatchedFilesParams) + @rpc_method( + name="workspace/didChangeWatchedFiles", + param_type=DidChangeWatchedFilesParams, + ) @threaded def _workspace_did_change_watched_files(self, changes: List[FileEvent], *args: Any, **kwargs: Any) -> None: changes = [e for e in changes if not e.uri.endswith("/globalStorage")] @@ -493,7 +501,10 @@ def add_file_watchers( entry = FileWatcherEntry(id=str(uuid.uuid4()), callback=callback, watchers=_watchers) - current_entry = next((e for e in self._file_watchers if e.watchers == _watchers), None) + current_entry = next( + (e for e in self._file_watchers if e.watchers == _watchers), + None, + ) if current_entry is not None: if callback not in self.did_change_watched_files: @@ -513,7 +524,7 @@ def add_file_watchers( and self.parent.client_capabilities.workspace.did_change_watched_files.dynamic_registration ): - def _done(f: Future[None]) -> None: + def _done(f: FutureEx[None]) -> None: if f.cancelled(): return exception = f.exception() diff --git a/packages/language_server/src/robotcode/language_server/common/protocol.py b/packages/language_server/src/robotcode/language_server/common/protocol.py index 547509e60..3033585ec 100644 --- a/packages/language_server/src/robotcode/language_server/common/protocol.py +++ b/packages/language_server/src/robotcode/language_server/common/protocol.py @@ -1,10 +1,10 @@ from __future__ import annotations import asyncio -from concurrent.futures import Future from threading import Event from typing import Any, ClassVar, Final, List, NamedTuple, Optional, Set, Union +from robotcode.core.concurrent import FutureEx from robotcode.core.event import event from robotcode.core.lsp.types import ( CancelParams, @@ -40,7 +40,9 @@ rpc_method, ) from robotcode.jsonrpc2.server import JsonRPCServer -from robotcode.language_server.common.parts.protocol_part import LanguageServerProtocolPart +from robotcode.language_server.common.parts.protocol_part import ( + LanguageServerProtocolPart, +) from .parts.code_action import CodeActionProtocolPart from .parts.code_lens import CodeLensProtocolPart @@ -181,7 +183,10 @@ def _collect_capabilities(self) -> ServerCapabilities: def start_parent_process_watcher(self) -> None: if self.parent_process_id and self.loop: - self.loop.call_later(self.PARENT_PROCESS_WATCHER_INTERVAL, self._parent_process_watcher) + self.loop.call_later( + self.PARENT_PROCESS_WATCHER_INTERVAL, + self._parent_process_watcher, + ) def _parent_process_watcher(self) -> None: if not self.parent_process_id: @@ -216,10 +221,15 @@ def _initialize( self.client_capabilities = capabilities - self._workspace = Workspace(self, root_uri=root_uri, root_path=root_path, workspace_folders=workspace_folders) + self._workspace = Workspace( + self, + root_uri=root_uri, + root_path=root_path, + workspace_folders=workspace_folders, + ) folders = ( - ", ".join((f"'{v.name}'" for v in self._workspace.workspace_folders)) + ", ".join(f"'{v.name}'" for v in self._workspace.workspace_folders) if self._workspace.workspace_folders else "" ) @@ -243,7 +253,9 @@ def _initialize( raise except BaseException as e: raise JsonRPCErrorException( - JsonRPCErrors.INTERNAL_ERROR, f"Can't start language server: {e}", InitializeError(retry=False) + JsonRPCErrors.INTERNAL_ERROR, + f"Can't start language server: {e}", + InitializeError(retry=False), ) from e return InitializeResult( @@ -303,22 +315,28 @@ def _set_trace(self, value: TraceValues, *args: Any, **kwargs: Any) -> None: def _cancel_request(self, id: Union[int, str], **kwargs: Any) -> None: self.cancel_request(id) - def register_capability(self, id: str, method: str, register_options: Optional[Any]) -> Future[None]: + def register_capability(self, id: str, method: str, register_options: Optional[Any]) -> FutureEx[None]: return self.register_capabilities([Registration(id=id, method=method, register_options=register_options)]) - def register_capabilities(self, registrations: List[Registration]) -> Future[None]: + def register_capabilities(self, registrations: List[Registration]) -> FutureEx[None]: if not registrations: - result: Future[None] = Future() + result: FutureEx[None] = FutureEx() result.set_result(None) return result - return self.send_request("client/registerCapability", RegistrationParams(registrations=registrations)) + return self.send_request( + "client/registerCapability", + RegistrationParams(registrations=registrations), + ) - def unregister_capability(self, id: str, method: str) -> Future[None]: + def unregister_capability(self, id: str, method: str) -> FutureEx[None]: return self.unregister_capabilities([Unregistration(id=id, method=method)]) - def unregister_capabilities(self, unregisterations: List[Unregistration]) -> Future[None]: + def unregister_capabilities(self, unregisterations: List[Unregistration]) -> FutureEx[None]: if not unregisterations: - result: Future[None] = Future() + result: FutureEx[None] = FutureEx() result.set_result(None) return result - return self.send_request("client/unregisterCapability", UnregistrationParams(unregisterations=unregisterations)) + return self.send_request( + "client/unregisterCapability", + UnregistrationParams(unregisterations=unregisterations), + ) diff --git a/packages/language_server/src/robotcode/language_server/common/text_document.py b/packages/language_server/src/robotcode/language_server/common/text_document.py index e71e3fba4..cae30118f 100644 --- a/packages/language_server/src/robotcode/language_server/common/text_document.py +++ b/packages/language_server/src/robotcode/language_server/common/text_document.py @@ -5,7 +5,17 @@ import io import threading import weakref -from typing import Any, Callable, Dict, Final, List, Optional, TypeVar, Union, cast +from typing import ( + Any, + Callable, + Dict, + Final, + List, + Optional, + TypeVar, + Union, + cast, +) from robotcode.core.event import event from robotcode.core.lsp.types import DocumentUri, Position, Range @@ -51,11 +61,17 @@ def position_to_utf16(lines: List[str], position: Position) -> Position: def range_from_utf16(lines: List[str], range: Range) -> Range: - return Range(start=position_from_utf16(lines, range.start), end=position_from_utf16(lines, range.end)) + return Range( + start=position_from_utf16(lines, range.start), + end=position_from_utf16(lines, range.end), + ) def range_to_utf16(lines: List[str], range: Range) -> Range: - return Range(start=position_to_utf16(lines, range.start), end=position_to_utf16(lines, range.end)) + return Range( + start=position_to_utf16(lines, range.start), + end=position_to_utf16(lines, range.end), + ) class InvalidRangeError(Exception): @@ -226,10 +242,7 @@ def __get_cache_reference(self, entry: Callable[..., Any], /, *, add_remove: boo return weakref.ref(entry, self.__remove_cache_entry if add_remove else None) - def get_cache_value( - self, - entry: Callable[[TextDocument], _T], - ) -> Optional[_T]: + def get_cache_value(self, entry: Callable[[TextDocument], _T]) -> Optional[_T]: reference = self.__get_cache_reference(entry) e = self._cache.get(reference, None) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/configuration.py b/packages/language_server/src/robotcode/language_server/robotframework/configuration.py index 13563d26a..89ad71145 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/configuration.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/configuration.py @@ -2,8 +2,14 @@ from enum import Enum from typing import Any, Dict, List, Optional, Tuple -from robotcode.language_server.common.parts.diagnostics import AnalysisProgressMode, DiagnosticsMode -from robotcode.language_server.common.parts.workspace import ConfigBase, config_section +from robotcode.language_server.common.parts.diagnostics import ( + AnalysisProgressMode, + DiagnosticsMode, +) +from robotcode.language_server.common.parts.workspace import ( + ConfigBase, + config_section, +) @config_section("robotcode.languageServer") 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 379b40289..e29a74c57 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 @@ -47,7 +47,10 @@ VariableDefinitionType, VariableNotFoundDefinition, ) -from robotcode.robot.diagnostics.library_doc import KeywordDoc, is_embedded_keyword +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, @@ -61,10 +64,7 @@ from .errors import DIAGNOSTICS_SOURCE_NAME, Error from .model_helper import ModelHelperMixin -from .namespace import ( - KeywordFinder, - Namespace, -) +from .namespace import KeywordFinder, Namespace if get_robot_version() < (7, 0): from robot.variables.search import VariableIterator @@ -140,7 +140,14 @@ def yield_argument_name_and_rest(self, node: ast.AST, token: Token) -> Iterator[ i = len(argument.value) for t in self.yield_argument_name_and_rest( - node, Token(token.type, token.value[i:], token.lineno, token.col_offset + i, token.error) + node, + Token( + token.type, + token.value[i:], + token.lineno, + token.col_offset + i, + token.error, + ), ): yield t else: @@ -163,7 +170,13 @@ def visit_Variable(self, node: Variable) -> None: # noqa: N802 r = range_from_token( strip_variable_token( - Token(name_token.type, name, name_token.lineno, name_token.col_offset, name_token.error) + Token( + name_token.type, + name, + name_token.lineno, + name_token.col_offset, + name_token.error, + ) ) ) @@ -180,7 +193,10 @@ def visit_Variable(self, node: Variable) -> None: # noqa: N802 return cmd_line_var = self.namespace.find_variable( - name, skip_commandline_variables=False, position=r.start, ignore_error=True + name, + skip_commandline_variables=False, + position=r.start, + ignore_error=True, ) if isinstance(cmd_line_var, CommandLineVariableDefinition): if self.namespace.document is not None: @@ -200,7 +216,7 @@ def visit(self, node: ast.AST) -> None: if isinstance(node, KeywordCall) and node.keyword: kw_doc = self.finder.find_keyword(node.keyword, raise_keyword_error=False) - if kw_doc is not None and kw_doc.longname in ["BuiltIn.Comment"]: + if kw_doc is not None and kw_doc.longname == "BuiltIn.Comment": severity = DiagnosticSeverity.HINT if isinstance(node, Statement) and not isinstance(node, (TestTemplate, Template)): @@ -245,7 +261,11 @@ def visit(self, node: ast.AST) -> None: if self.namespace.document is not None: if isinstance(var, EnvironmentVariableDefinition): - var_token.value, _, _ = var_token.value.partition("=") + ( + var_token.value, + _, + _, + ) = var_token.value.partition("=") var_range = range_from_token(var_token) @@ -256,21 +276,29 @@ def visit(self, node: ast.AST) -> None: skip_commandline_variables=True, ignore_error=True, ) - if suite_var is not None and suite_var.type not in [ - VariableDefinitionType.VARIABLE - ]: + if suite_var is not None and suite_var.type != VariableDefinitionType.VARIABLE: suite_var = None if var.name_range != var_range: self._variable_references[var].add( - Location(self.namespace.document.document_uri, var_range) + Location( + self.namespace.document.document_uri, + var_range, + ) ) if suite_var is not None: self._variable_references[suite_var].add( - Location(self.namespace.document.document_uri, var_range) + Location( + self.namespace.document.document_uri, + var_range, + ) ) - if token1.type in [Token.ASSIGN] and isinstance( - var, (LocalVariableDefinition, ArgumentDefinition) + if token1.type == Token.ASSIGN and isinstance( + var, + ( + LocalVariableDefinition, + ArgumentDefinition, + ), ): self._local_variable_assignments[var].add(var_range) @@ -310,7 +338,10 @@ def visit(self, node: ast.AST) -> None: if var.name_range != var_range: self._variable_references[var].add( - Location(self.namespace.document.document_uri, range_from_token(var_token)) + Location( + self.namespace.document.document_uri, + range_from_token(var_token), + ) ) if isinstance(var, CommandLineVariableDefinition): @@ -319,9 +350,12 @@ def visit(self, node: ast.AST) -> None: skip_commandline_variables=True, ignore_error=True, ) - if suite_var is not None and suite_var.type in [VariableDefinitionType.VARIABLE]: + if suite_var is not None and suite_var.type == VariableDefinitionType.VARIABLE: self._variable_references[suite_var].add( - Location(self.namespace.document.document_uri, range_from_token(var_token)) + Location( + self.namespace.document.document_uri, + range_from_token(var_token), + ) ) super().visit(node) @@ -382,7 +416,14 @@ def _analyze_keyword_call( if not allow_variables and not is_not_variable_token(keyword_token): return None - if self.finder.find_keyword(keyword_token.value, raise_keyword_error=False, handle_bdd_style=False) is None: + if ( + self.finder.find_keyword( + keyword_token.value, + raise_keyword_error=False, + handle_bdd_style=False, + ) + is None + ): keyword_token = self.strip_bdd_prefix(self.namespace, keyword_token) kw_range = range_from_token(keyword_token) @@ -394,7 +435,10 @@ def _analyze_keyword_call( result = self.finder.find_keyword(keyword, raise_keyword_error=False) if keyword is not None: - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(self.namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(self.namespace, keyword_token) if lib_entry and kw_namespace: r = range_from_token(keyword_token) @@ -460,19 +504,11 @@ def _analyze_keyword_call( ), range=Range( start=Position( - line=err.line_no - 1 - if err.line_no is not None - else result.line_no - if result.line_no >= 0 - else 0, + line=err.line_no - 1 if err.line_no is not None else max(result.line_no, 0), character=0, ), end=Position( - line=err.line_no - 1 - if err.line_no is not None - else result.line_no - if result.line_no >= 0 - else 0, + line=err.line_no - 1 if err.line_no is not None else max(result.line_no, 0), character=0, ), ), @@ -565,7 +601,10 @@ def _analyze_keyword_call( ]: tokens = argument_tokens if tokens and (token := tokens[0]): - for var_token, var in self.iter_expression_variables_from_token( + for ( + var_token, + var, + ) in self.iter_expression_variables_from_token( token, self.namespace, self.node_stack, @@ -583,7 +622,10 @@ def _analyze_keyword_call( else: if self.namespace.document is not None: self._variable_references[var].add( - Location(self.namespace.document.document_uri, range_from_token(var_token)) + Location( + self.namespace.document.document_uri, + range_from_token(var_token), + ) ) if isinstance(var, CommandLineVariableDefinition): @@ -592,9 +634,12 @@ def _analyze_keyword_call( skip_commandline_variables=True, ignore_error=True, ) - if suite_var is not None and suite_var.type in [VariableDefinitionType.VARIABLE]: + if suite_var is not None and suite_var.type == VariableDefinitionType.VARIABLE: self._variable_references[suite_var].add( - Location(self.namespace.document.document_uri, range_from_token(var_token)) + Location( + self.namespace.document.document_uri, + range_from_token(var_token), + ) ) if result.argument_definitions: for arg in argument_tokens: @@ -607,7 +652,10 @@ def _analyze_keyword_call( if arg_def is not None: name_token = Token(Token.ARGUMENT, name, arg.lineno, arg.col_offset) self._variable_references[arg_def].add( - Location(self.namespace.document.document_uri, range_from_token(name_token)) + Location( + self.namespace.document.document_uri, + range_from_token(name_token), + ) ) if result is not None and analyse_run_keywords: @@ -616,7 +664,10 @@ def _analyze_keyword_call( return result def _analyse_run_keyword( - self, keyword_doc: Optional[KeywordDoc], node: ast.AST, argument_tokens: List[Token] + self, + keyword_doc: Optional[KeywordDoc], + node: ast.AST, + argument_tokens: List[Token], ) -> List[Token]: if keyword_doc is None or not keyword_doc.is_any_run_keyword(): return argument_tokens @@ -782,9 +833,17 @@ def visit_Fixture(self, node: Fixture) -> None: # noqa: N802 def visit_TestTemplate(self, node: TestTemplate) -> None: # noqa: N802 keyword_token = node.get_token(Token.NAME) - if keyword_token is not None and keyword_token.value.upper() not in ("", "NONE"): + if keyword_token is not None and keyword_token.value.upper() not in ( + "", + "NONE", + ): self._analyze_keyword_call( - node.value, node, keyword_token, [], analyse_run_keywords=False, allow_variables=True + node.value, + node, + keyword_token, + [], + analyse_run_keywords=False, + allow_variables=True, ) self.test_template = node @@ -793,9 +852,17 @@ def visit_TestTemplate(self, node: TestTemplate) -> None: # noqa: N802 def visit_Template(self, node: Template) -> None: # noqa: N802 keyword_token = node.get_token(Token.NAME) - if keyword_token is not None and keyword_token.value.upper() not in ("", "NONE"): + if keyword_token is not None and keyword_token.value.upper() not in ( + "", + "NONE", + ): self._analyze_keyword_call( - node.value, node, keyword_token, [], analyse_run_keywords=False, allow_variables=True + node.value, + node, + keyword_token, + [], + analyse_run_keywords=False, + allow_variables=True, ) self.template = node self.generic_visit(node) @@ -811,7 +878,12 @@ def visit_KeywordCall(self, node: KeywordCall) -> None: # noqa: N802 code=Error.KEYWORD_NAME_EMPTY, ) else: - self._analyze_keyword_call(node.keyword, node, keyword_token, [e for e in node.get_tokens(Token.ARGUMENT)]) + self._analyze_keyword_call( + node.keyword, + node, + keyword_token, + [e for e in node.get_tokens(Token.ARGUMENT)], + ) if not self.current_testcase_or_keyword_name: self.append_diagnostics( 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 9e342137b..a3eac4a3f 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 @@ -34,10 +34,19 @@ from robotcode.core.utils.logging import LoggingDescriptor from robotcode.core.utils.path import path_is_relative_to from robotcode.language_server.common.decorators import language_id -from robotcode.language_server.common.parts.workspace import FileWatcherEntry, Workspace +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.diagnostics.entities import CommandLineVariableDefinition, VariableDefinition +from robotcode.language_server.robotframework.configuration import ( + CacheSaveLocation, + RobotCodeConfig, +) +from robotcode.robot.diagnostics.entities import ( + CommandLineVariableDefinition, + VariableDefinition, +) from robotcode.robot.diagnostics.library_doc import ( ROBOT_LIBRARY_PACKAGE, CompleteResult, @@ -65,7 +74,9 @@ from ...__version__ import __version__ if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) from .namespace import Namespace @@ -97,10 +108,7 @@ def __hash__(self) -> int: class _ImportEntry(ABC): - def __init__( - self, - parent: "ImportsManager", - ) -> None: + def __init__(self, parent: "ImportsManager") -> None: self.parent = parent self.references: weakref.WeakSet[Any] = weakref.WeakSet() self.file_watchers: List[FileWatcherEntry] = [] @@ -243,7 +251,8 @@ def _update(self) -> None: if source_or_origin is not None: self.file_watchers.append( self.parent.parent_protocol.workspace.add_file_watchers( - self.parent.did_change_watched_files, [str(Path(source_or_origin).parent.joinpath("**"))] + self.parent.did_change_watched_files, + [str(Path(source_or_origin).parent.joinpath("**"))], ) ) @@ -489,7 +498,12 @@ 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 @@ -575,7 +589,17 @@ def get_command_line_variables(self) -> List[VariableDefinition]: command_line_vars: List[VariableDefinition] = [] command_line_vars += [ - CommandLineVariableDefinition(0, 0, 0, 0, "", f"${{{k}}}", None, has_value=True, value=v) + CommandLineVariableDefinition( + 0, + 0, + 0, + 0, + "", + f"${{{k}}}", + None, + has_value=True, + value=v, + ) for k, v in { **{k1: v1 for k1, v1 in (self.parent_protocol.profile.variables or {}).items()}, **self.config.robot.variables, @@ -749,7 +773,12 @@ def did_change_watched_files(self, sender: Any, changes: List[FileEvent]) -> Non self.variables_changed(self, [v for (_, _, v) in variables_changed if v is not None]) - def __remove_library_entry(self, entry_key: _LibrariesEntryKey, entry: _LibrariesEntry, now: bool = False) -> None: + def __remove_library_entry( + self, + entry_key: _LibrariesEntryKey, + entry: _LibrariesEntry, + now: bool = False, + ) -> None: try: if len(entry.references) == 0 or now: self._logger.debug(lambda: f"Remove Library Entry {entry_key}") @@ -764,7 +793,12 @@ def __remove_library_entry(self, entry_key: _LibrariesEntryKey, entry: _Librarie finally: self._library_files_cache.clear() - def __remove_resource_entry(self, entry_key: _ResourcesEntryKey, entry: _ResourcesEntry, now: bool = False) -> None: + def __remove_resource_entry( + self, + entry_key: _ResourcesEntryKey, + entry: _ResourcesEntry, + now: bool = False, + ) -> None: try: if len(entry.references) == 0 or now: self._logger.debug(lambda: f"Remove Resource Entry {entry_key}") @@ -780,7 +814,10 @@ def __remove_resource_entry(self, entry_key: _ResourcesEntryKey, entry: _Resourc self._resource_files_cache.clear() def __remove_variables_entry( - self, entry_key: _VariablesEntryKey, entry: _VariablesEntry, now: bool = False + self, + entry_key: _VariablesEntryKey, + entry: _VariablesEntry, + now: bool = False, ) -> None: try: if len(entry.references) == 0 or now: @@ -926,10 +963,20 @@ def get_variables_meta( return None, name - def find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str: + def find_library( + self, + name: str, + base_dir: str, + variables: Optional[Dict[str, Any]] = None, + ) -> str: return self._library_files_cache.get(self._find_library, name, base_dir, variables) - def _find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str: + def _find_library( + self, + name: str, + base_dir: str, + variables: Optional[Dict[str, Any]] = None, + ) -> str: from robot.libraries import STDLIBS from robot.variables.search import contains_variable @@ -953,13 +1000,21 @@ def _find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, return result def find_resource( - self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None + self, + name: str, + base_dir: str, + file_type: str = "Resource", + variables: Optional[Dict[str, Any]] = None, ) -> str: return self._resource_files_cache.get(self.__find_resource, name, base_dir, file_type, variables) @_logger.call def __find_resource( - self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None + self, + name: str, + base_dir: str, + file_type: str = "Resource", + variables: Optional[Dict[str, Any]] = None, ) -> str: from robot.variables.search import contains_variable @@ -984,7 +1039,11 @@ def find_variables( resolve_command_line_vars: bool = True, ) -> str: return self._variables_files_cache.get( - self.__find_variables, name, base_dir, variables, resolve_command_line_vars + self.__find_variables, + name, + base_dir, + variables, + resolve_command_line_vars, ) @_logger.call @@ -1032,11 +1091,7 @@ def get_libdoc_for_library_import( sentinel: Any = None, variables: Optional[Dict[str, Any]] = None, ) -> LibraryDoc: - source = self.find_library( - name, - base_dir, - variables, - ) + source = self.find_library(name, base_dir, variables) def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: str) -> LibraryDoc: meta, source = self.get_library_meta(name, base_dir, variables) @@ -1051,11 +1106,11 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st try: saved_meta = from_json(meta_file.read_text("utf-8"), LibraryMetaData) if saved_meta == meta: - spec_path = Path(self.lib_doc_cache_path, meta.filepath_base + ".spec.json") - return from_json( - spec_path.read_text("utf-8"), - LibraryDoc, + spec_path = Path( + self.lib_doc_cache_path, + meta.filepath_base + ".spec.json", ) + return from_json(spec_path.read_text("utf-8"), LibraryDoc) except (SystemExit, KeyboardInterrupt): raise except BaseException as e: @@ -1091,8 +1146,14 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st self._logger.warning(lambda: f"stdout captured at loading library {name}{args!r}:\n{result.stdout}") try: if meta is not None: - meta_file = Path(self.lib_doc_cache_path, meta.filepath_base + ".meta.json") - spec_file = Path(self.lib_doc_cache_path, meta.filepath_base + ".spec.json") + meta_file = Path( + self.lib_doc_cache_path, + meta.filepath_base + ".meta.json", + ) + spec_file = Path( + self.lib_doc_cache_path, + meta.filepath_base + ".spec.json", + ) spec_file.parent.mkdir(parents=True, exist_ok=True) try: @@ -1151,7 +1212,11 @@ def get_libdoc_from_model( append_model_errors: bool = True, ) -> LibraryDoc: return get_model_doc( - model=model, source=source, model_type=model_type, scope=scope, append_model_errors=append_model_errors + model=model, + source=source, + model_type=model_type, + scope=scope, + append_model_errors=append_model_errors, ) @_logger.call @@ -1183,18 +1248,21 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st self._logger.debug(lambda: f"Load variables {source}{args!r}") if meta is not None: - meta_file = Path(self.variables_doc_cache_path, meta.filepath_base + ".meta.json") + meta_file = Path( + self.variables_doc_cache_path, + meta.filepath_base + ".meta.json", + ) if meta_file.exists(): try: spec_path = None try: saved_meta = from_json(meta_file.read_text("utf-8"), LibraryMetaData) if saved_meta == meta: - spec_path = Path(self.variables_doc_cache_path, meta.filepath_base + ".spec.json") - return from_json( - spec_path.read_text("utf-8"), - VariablesDoc, + spec_path = Path( + self.variables_doc_cache_path, + meta.filepath_base + ".spec.json", ) + return from_json(spec_path.read_text("utf-8"), VariablesDoc) except (SystemExit, KeyboardInterrupt): raise except BaseException as e: @@ -1230,8 +1298,14 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st try: if meta is not None: - meta_file = Path(self.variables_doc_cache_path, meta.filepath_base + ".meta.json") - spec_file = Path(self.variables_doc_cache_path, meta.filepath_base + ".spec.json") + meta_file = Path( + self.variables_doc_cache_path, + meta.filepath_base + ".meta.json", + ) + spec_file = Path( + self.variables_doc_cache_path, + meta.filepath_base + ".spec.json", + ) spec_file.parent.mkdir(parents=True, exist_ok=True) try: @@ -1262,7 +1336,12 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st with self._variables_lock: if entry_key not in self._variables: self._variables[entry_key] = _VariablesEntry( - name, resolved_args, str(self.folder.to_path()), base_dir, self, _get_libdoc + name, + resolved_args, + str(self.folder.to_path()), + base_dir, + self, + _get_libdoc, ) entry = self._variables[entry_key] @@ -1275,7 +1354,11 @@ def _get_libdoc(name: str, args: Tuple[Any, ...], working_dir: str, base_dir: st @_logger.call def _get_entry_for_resource_import( - self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None + self, + name: str, + base_dir: str, + sentinel: Any = None, + variables: Optional[Dict[str, Any]] = None, ) -> _ResourcesEntry: source = self.find_resource(name, base_dir, variables=variables) @@ -1329,14 +1412,21 @@ def get_namespace_for_resource_import( return entry.get_namespace() def get_libdoc_for_resource_import( - self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None + self, + name: str, + base_dir: str, + sentinel: Any = None, + variables: Optional[Dict[str, Any]] = None, ) -> LibraryDoc: entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables) return entry.get_libdoc() def complete_library_import( - self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None + self, + name: Optional[str], + base_dir: str = ".", + variables: Optional[Dict[str, Any]] = None, ) -> List[CompleteResult]: return complete_library_import( name, @@ -1347,7 +1437,10 @@ def complete_library_import( ) def complete_resource_import( - self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None + self, + name: Optional[str], + base_dir: str = ".", + variables: Optional[Dict[str, Any]] = None, ) -> Optional[List[CompleteResult]]: return complete_resource_import( name, @@ -1358,7 +1451,10 @@ def complete_resource_import( ) def complete_variables_import( - self, name: Optional[str], base_dir: str = ".", variables: Optional[Dict[str, Any]] = None + self, + name: Optional[str], + base_dir: str = ".", + variables: Optional[Dict[str, Any]] = None, ) -> Optional[List[CompleteResult]]: return complete_variables_import( name, @@ -1368,7 +1464,12 @@ def complete_variables_import( variables, ) - def resolve_variable(self, name: str, base_dir: str = ".", variables: Optional[Dict[str, Any]] = None) -> Any: + def resolve_variable( + self, + name: str, + base_dir: str = ".", + variables: Optional[Dict[str, Any]] = None, + ) -> Any: return resolve_variable( name, str(self.folder.to_path()), 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 14d812e12..748595575 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 @@ -43,10 +43,7 @@ whitespace_from_begin_of_token, ) -from .namespace import ( - DEFAULT_BDD_PREFIXES, - Namespace, -) +from .namespace import DEFAULT_BDD_PREFIXES, Namespace class ModelHelperMixin: @@ -146,12 +143,18 @@ def skip_args() -> None: inner_keyword_doc = namespace.find_keyword(unescape(argument_tokens[1].value)) if position.is_in_range(range_from_token(argument_tokens[1])): - return (inner_keyword_doc, argument_tokens[1]), argument_tokens[2:] + return ( + inner_keyword_doc, + argument_tokens[1], + ), argument_tokens[2:] argument_tokens = argument_tokens[2:] inner_keyword_doc_and_args = cls.get_run_keyword_keyworddoc_and_token_from_position( - inner_keyword_doc, argument_tokens, namespace, position + inner_keyword_doc, + argument_tokens, + namespace, + position, ) if inner_keyword_doc_and_args[0] is not None: @@ -166,12 +169,18 @@ def skip_args() -> None: inner_keyword_doc = namespace.find_keyword(unescape(argument_tokens[2].value)) if position.is_in_range(range_from_token(argument_tokens[2])): - return (inner_keyword_doc, argument_tokens[2]), argument_tokens[3:] + return ( + inner_keyword_doc, + argument_tokens[2], + ), argument_tokens[3:] argument_tokens = argument_tokens[3:] inner_keyword_doc_and_args = cls.get_run_keyword_keyworddoc_and_token_from_position( - inner_keyword_doc, argument_tokens, namespace, position + inner_keyword_doc, + argument_tokens, + namespace, + position, ) if inner_keyword_doc_and_args[0] is not None: @@ -220,7 +229,10 @@ def get_namespace_info_from_keyword_token( for lib, keyword in iter_over_keyword_names_and_owners(keyword_token.value): if lib is not None: - lib_entries = next((v for k, v in (namespace.get_namespaces()).items() if k == lib), None) + lib_entries = next( + (v for k, v in (namespace.get_namespaces()).items() if k == lib), + None, + ) if lib_entries is not None: kw_namespace = lib lib_entry = next( @@ -270,14 +282,17 @@ def iter_expression_variables_from_token( if var is not None: yield sub_token, var elif return_not_found: - yield sub_token, VariableNotFoundDefinition( - sub_token.lineno, - sub_token.col_offset, - sub_token.lineno, - sub_token.end_col_offset, - namespace.source, - tokval, + yield ( sub_token, + VariableNotFoundDefinition( + sub_token.lineno, + sub_token.col_offset, + sub_token.lineno, + sub_token.end_col_offset, + namespace.source, + tokval, + sub_token, + ), ) variable_started = False if tokval == "$": @@ -286,7 +301,9 @@ def iter_expression_variables_from_token( pass @staticmethod - def remove_index_from_variable_token(token: Token) -> Tuple[Token, Optional[Token]]: + def remove_index_from_variable_token( + token: Token, + ) -> Tuple[Token, Optional[Token]]: def escaped(i: int) -> bool: return bool(token.value[-i - 3 : -i - 2] == "\\") @@ -342,7 +359,12 @@ def _tokenize_variables( if var is not None: yield var if rest is not None: - yield from cls._tokenize_variables(rest, identifiers, ignore_errors, extra_types=extra_types) + yield from cls._tokenize_variables( + rest, + identifiers, + ignore_errors, + extra_types=extra_types, + ) else: yield t @@ -387,14 +409,17 @@ def iter_token( ): yield v elif base == "" and return_not_found: - yield sub_token, VariableNotFoundDefinition( - sub_token.lineno, - sub_token.col_offset, - sub_token.lineno, - sub_token.end_col_offset, - namespace.source, - sub_token.value, + yield ( sub_token, + VariableNotFoundDefinition( + sub_token.lineno, + sub_token.col_offset, + sub_token.lineno, + sub_token.end_col_offset, + namespace.source, + sub_token.value, + sub_token, + ), ) return @@ -462,7 +487,12 @@ def iter_token( skip_commandline_variables=skip_commandline_variables, ignore_error=True, ) - sub_sub_token = Token(sub_token.type, name, sub_token.lineno, sub_token.col_offset) + sub_sub_token = Token( + sub_token.type, + name, + sub_token.lineno, + sub_token.col_offset, + ) if var is not None: yield strip_variable_token(sub_sub_token), var continue @@ -472,24 +502,30 @@ def iter_token( if contains_variable(sub_token.value[2:-1]): continue else: - yield strip_variable_token(sub_sub_token), VariableNotFoundDefinition( - sub_sub_token.lineno, - sub_sub_token.col_offset, - sub_sub_token.lineno, - sub_sub_token.end_col_offset, - namespace.source, - name, - sub_sub_token, + yield ( + strip_variable_token(sub_sub_token), + VariableNotFoundDefinition( + sub_sub_token.lineno, + sub_sub_token.col_offset, + sub_sub_token.lineno, + sub_sub_token.end_col_offset, + namespace.source, + name, + sub_sub_token, + ), ) if return_not_found: - yield strip_variable_token(sub_token), VariableNotFoundDefinition( - sub_token.lineno, - sub_token.col_offset, - sub_token.lineno, - sub_token.end_col_offset, - namespace.source, - sub_token.value, - sub_token, + yield ( + strip_variable_token(sub_token), + VariableNotFoundDefinition( + sub_token.lineno, + sub_token.col_offset, + sub_token.lineno, + sub_token.end_col_offset, + namespace.source, + sub_token.value, + sub_token, + ), ) else: yield token_or_var @@ -734,7 +770,11 @@ def get_argument_info_at_position( ( i for i, v in enumerate(kw_arguments) - if v.kind in [KeywordArgumentKind.POSITIONAL_ONLY, KeywordArgumentKind.POSITIONAL_OR_NAMED] + if v.kind + in [ + KeywordArgumentKind.POSITIONAL_ONLY, + KeywordArgumentKind.POSITIONAL_OR_NAMED, + ] and i == argument_index ), -1, @@ -744,11 +784,13 @@ def get_argument_info_at_position( else: if need_named: argument_index = next( - (i for i, v in enumerate(kw_arguments) if v.kind == KeywordArgumentKind.VAR_NAMED), -1 + (i for i, v in enumerate(kw_arguments) if v.kind == KeywordArgumentKind.VAR_NAMED), + -1, ) else: argument_index = next( - (i for i, v in enumerate(kw_arguments) if v.kind == KeywordArgumentKind.VAR_POSITIONAL), -1 + (i for i, v in enumerate(kw_arguments) if v.kind == KeywordArgumentKind.VAR_POSITIONAL), + -1, ) if argument_index >= len(kw_arguments): 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 dbcf15dc1..103e1a2e2 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 @@ -27,12 +27,23 @@ from robot.errors import VariableError from robot.libraries import STDLIBS from robot.parsing.lexer.tokens import Token -from robot.parsing.model.blocks import Keyword, SettingSection, TestCase, VariableSection +from robot.parsing.model.blocks import ( + Keyword, + SettingSection, + TestCase, + VariableSection, +) from robot.parsing.model.statements import Arguments, Statement from robot.parsing.model.statements import LibraryImport as RobotLibraryImport from robot.parsing.model.statements import ResourceImport as RobotResourceImport -from robot.parsing.model.statements import VariablesImport as RobotVariablesImport -from robot.variables.search import is_scalar_assign, is_variable, search_variable +from robot.parsing.model.statements import ( + VariablesImport as RobotVariablesImport, +) +from robot.variables.search import ( + is_scalar_assign, + is_variable, + search_variable, +) from robotcode.core.event import event from robotcode.core.lsp.types import ( CodeDescription, @@ -136,13 +147,25 @@ def visit_Variable(self, node: Statement) -> None: # noqa: N802 values = node.get_values(Token.ARGUMENT) has_value = bool(values) - value = tuple(s.replace("${CURDIR}", str(Path(self.source).parent).replace("\\", "\\\\")) for s in values) + value = tuple( + s.replace( + "${CURDIR}", + str(Path(self.source).parent).replace("\\", "\\\\"), + ) + for s in values + ) self._results.append( VariableDefinition( name=name, name_token=strip_variable_token( - Token(name_token.type, name, name_token.lineno, name_token.col_offset, name_token.error) + Token( + name_token.type, + name, + name_token.lineno, + name_token.col_offset, + name_token.error, + ) ), line_no=node.lineno, col_offset=node.col_offset, @@ -158,7 +181,11 @@ def visit_Variable(self, node: Statement) -> None: # noqa: N802 class BlockVariableVisitor(Visitor): def __init__( - self, library_doc: LibraryDoc, source: str, position: Optional[Position] = None, in_args: bool = True + self, + library_doc: LibraryDoc, + source: str, + position: Optional[Position] = None, + in_args: bool = True, ) -> None: super().__init__() self.library_doc = library_doc @@ -675,14 +702,18 @@ def get_keyword_references(self) -> Dict[KeywordDoc, Set[Location]]: return self._keyword_references - def get_variable_references(self) -> Dict[VariableDefinition, Set[Location]]: + def get_variable_references( + self, + ) -> Dict[VariableDefinition, Set[Location]]: self.ensure_initialized() self._analyze() return self._variable_references - def get_local_variable_assignments(self) -> Dict[VariableDefinition, Set[Range]]: + def get_local_variable_assignments( + self, + ) -> Dict[VariableDefinition, Set[Range]]: self.ensure_initialized() self._analyze() @@ -736,8 +767,7 @@ def get_library_doc(self) -> LibraryDoc: self.model, self.source, model_type="RESOURCE", - append_model_errors=self.document_type is not None - and self.document_type in [DocumentType.RESOURCE], + append_model_errors=self.document_type is not None and self.document_type == DocumentType.RESOURCE, ) return self._library_doc @@ -796,7 +826,10 @@ def ensure_initialized(self) -> bool: self._import_default_libraries(variables) self._import_imports( - imports, str(Path(self.source).parent), top_level=True, variables=variables + imports, + str(Path(self.source).parent), + top_level=True, + variables=variables, ) if self.document is not None: @@ -895,7 +928,10 @@ def yield_variables( yielded: Dict[VariableMatcher, VariableDefinition] = {} test_or_keyword_nodes = list( - itertools.dropwhile(lambda v: not isinstance(v, (TestCase, Keyword)), nodes if nodes else []) + itertools.dropwhile( + lambda v: not isinstance(v, (TestCase, Keyword)), + nodes if nodes else [], + ) ) test_or_keyword = test_or_keyword_nodes[0] if test_or_keyword_nodes else None @@ -914,7 +950,7 @@ def yield_variables( ], self.get_global_variables(), ): - if var.matcher not in yielded.keys(): + if var.matcher not in yielded: if skip_commandline_variables and isinstance(var, CommandLineVariableDefinition): continue @@ -923,7 +959,9 @@ def yield_variables( yield var.matcher, var def get_resolvable_variables( - self, nodes: Optional[List[ast.AST]] = None, position: Optional[Position] = None + self, + nodes: Optional[List[ast.AST]] = None, + position: Optional[Position] = None, ) -> Dict[str, Any]: return { v.name: v.value @@ -932,7 +970,9 @@ def get_resolvable_variables( } def get_variable_matchers( - self, nodes: Optional[List[ast.AST]] = None, position: Optional[Position] = None + self, + nodes: Optional[List[ast.AST]] = None, + position: Optional[Position] = None, ) -> Dict[VariableMatcher, VariableDefinition]: self.ensure_initialized() @@ -952,7 +992,14 @@ def find_variable( if name[:2] == "%{" and name[-1] == "}": var_name, _, default_value = name[2:-1].partition("=") return EnvironmentVariableDefinition( - 0, 0, 0, 0, "", f"%{{{var_name}}}", None, default_value=default_value or None + 0, + 0, + 0, + 0, + "", + f"%{{{var_name}}}", + None, + default_value=default_value or None, ) try: @@ -991,7 +1038,12 @@ def _import( raise NameSpaceError("Library setting requires value.") result = self._get_library_entry( - value.name, value.args, value.alias, base_dir, sentinel=value, variables=variables + value.name, + value.args, + value.alias, + base_dir, + sentinel=value, + variables=variables, ) result.import_range = value.range result.import_source = value.source @@ -1026,7 +1078,10 @@ def _import( source=DIAGNOSTICS_SOURCE_NAME, related_information=[ DiagnosticRelatedInformation( - location=Location(str(Uri.from_path(value.source)), value.range), + location=Location( + str(Uri.from_path(value.source)), + value.range, + ), message=f"'{Path(self.source).name}' is also imported here.", ) ] @@ -1035,7 +1090,12 @@ def _import( code=Error.POSSIBLE_CIRCULAR_IMPORT, ) else: - result = self._get_resource_entry(value.name, base_dir, sentinel=value, variables=variables) + result = self._get_resource_entry( + value.name, + base_dir, + sentinel=value, + variables=variables, + ) result.import_range = value.range result.import_source = value.source @@ -1063,7 +1123,11 @@ def _import( raise NameSpaceError("Variables setting requires value.") result = self._get_variables_entry( - value.name, value.args, base_dir, sentinel=value, variables=variables + value.name, + value.args, + base_dir, + sentinel=value, + variables=variables, ) result.import_range = value.range @@ -1090,17 +1154,19 @@ def _import( start=Position( line=err.line_no - 1 if err.line_no is not None - else result.library_doc.line_no - if result.library_doc.line_no >= 0 - else 0, + else max( + result.library_doc.line_no, + 0, + ), character=0, ), end=Position( line=err.line_no - 1 if err.line_no is not None - else result.library_doc.line_no - if result.library_doc.line_no >= 0 - else 0, + else max( + result.library_doc.line_no, + 0, + ), character=0, ), ), @@ -1113,7 +1179,8 @@ def _import( code=Error.IMPORT_CONTAINS_ERRORS, ) for err in filter( - lambda e: e.source is None or not Path(e.source).is_absolute(), result.library_doc.errors + lambda e: e.source is None or not Path(e.source).is_absolute(), + result.library_doc.errors, ): self.append_diagnostics( range=value.range, @@ -1318,7 +1385,12 @@ def _import_default_libraries(self, variables: Optional[Dict[str, Any]] = None) def _import_lib(library: str, variables: Optional[Dict[str, Any]] = None) -> Optional[LibraryEntry]: try: return self._get_library_entry( - library, (), None, str(Path(self.source).parent), is_default_library=True, variables=variables + library, + (), + None, + str(Path(self.source).parent), + is_default_library=True, + variables=variables, ) except (SystemExit, KeyboardInterrupt): raise @@ -1361,7 +1433,13 @@ def _get_library_entry( variables=variables or self.get_resolvable_variables(), ) - return LibraryEntry(name=library_doc.name, import_name=name, library_doc=library_doc, args=args, alias=alias) + return LibraryEntry( + name=library_doc.name, + import_name=name, + library_doc=library_doc, + args=args, + alias=alias, + ) @_logger.call def get_imported_library_libdoc( @@ -1380,9 +1458,17 @@ def get_imported_library_libdoc( @_logger.call def _get_resource_entry( - self, name: str, base_dir: str, *, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None + self, + name: str, + base_dir: str, + *, + sentinel: Any = None, + variables: Optional[Dict[str, Any]] = None, ) -> ResourceEntry: - namespace, library_doc = self.imports_manager.get_namespace_and_libdoc_for_resource_import( + ( + namespace, + library_doc, + ) = self.imports_manager.get_namespace_and_libdoc_for_resource_import( name, base_dir, sentinel=sentinel, @@ -1429,7 +1515,11 @@ def _get_variables_entry( ) return VariablesEntry( - name=library_doc.name, import_name=name, library_doc=library_doc, args=args, variables=library_doc.variables + name=library_doc.name, + import_name=name, + library_doc=library_doc, + args=args, + variables=library_doc.variables, ) @_logger.call @@ -1493,7 +1583,7 @@ def get_keywords(self) -> List[KeywordDoc]: else: self._logger.debug( lambda: f"end collecting {len(self._keywords) if self._keywords else 0}" - f" keywords in {time.monotonic()-current_time}s analyze {i} keywords" + f" keywords in {time.monotonic() - current_time}s analyze {i} keywords" ) return self._keywords @@ -1514,7 +1604,17 @@ def append_diagnostics( return self._diagnostics.append( - Diagnostic(range, message, severity, code, code_description, source, tags, related_information, data) + Diagnostic( + range, + message, + severity, + code, + code_description, + source, + tags, + related_information, + data, + ) ) @_logger.call(condition=lambda self: not self._analyzed) @@ -1591,11 +1691,19 @@ def create_finder(self) -> KeywordFinder: @_logger.call(condition=lambda self, name, **kwargs: self._finder is not None and name not in self._finder._cache) def find_keyword( - self, name: Optional[str], *, raise_keyword_error: bool = True, handle_bdd_style: bool = True + self, + name: Optional[str], + *, + raise_keyword_error: bool = True, + handle_bdd_style: bool = True, ) -> Optional[KeywordDoc]: finder = self._finder if self._finder is not None else self.get_finder() - return finder.find_keyword(name, raise_keyword_error=raise_keyword_error, handle_bdd_style=handle_bdd_style) + return finder.find_keyword( + name, + raise_keyword_error=raise_keyword_error, + handle_bdd_style=handle_bdd_style, + ) @classmethod def get_ignored_lines(cls, document: TextDocument) -> List[int]: @@ -1616,7 +1724,10 @@ def __get_ignored_lines(document: TextDocument) -> List[int]: @classmethod def should_ignore(cls, document: Optional[TextDocument], range: Range) -> bool: - return cls.__should_ignore(cls.get_ignored_lines(document) if document is not None else [], range) + return cls.__should_ignore( + cls.get_ignored_lines(document) if document is not None else [], + range, + ) def _should_ignore(self, range: Range) -> bool: if self._ignored_lines is None: @@ -1652,7 +1763,12 @@ def __init__(self, namespace: Namespace, library_doc: LibraryDoc) -> None: self.diagnostics: List[DiagnosticsEntry] = [] self.multiple_keywords_result: Optional[List[KeywordDoc]] = None self._cache: Dict[ - Tuple[Optional[str], bool], Tuple[Optional[KeywordDoc], List[DiagnosticsEntry], Optional[List[KeywordDoc]]] + Tuple[Optional[str], bool], + Tuple[ + Optional[KeywordDoc], + List[DiagnosticsEntry], + Optional[List[KeywordDoc]], + ], ] = {} self.handle_bdd_style = True self._all_keywords: Optional[List[LibraryEntry]] = None @@ -1664,7 +1780,11 @@ def reset_diagnostics(self) -> None: self.multiple_keywords_result = None def find_keyword( - self, name: Optional[str], *, raise_keyword_error: bool = False, handle_bdd_style: bool = True + self, + name: Optional[str], + *, + raise_keyword_error: bool = False, + handle_bdd_style: bool = True, ) -> Optional[KeywordDoc]: try: self.reset_diagnostics() @@ -1698,7 +1818,11 @@ def find_keyword( result = None self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR)) - self._cache[(name, self.handle_bdd_style)] = (result, self.diagnostics, self.multiple_keywords_result) + self._cache[(name, self.handle_bdd_style)] = ( + result, + self.diagnostics, + self.multiple_keywords_result, + ) return result except CancelSearchError: @@ -1707,12 +1831,20 @@ def find_keyword( def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]: if not name: self.diagnostics.append( - DiagnosticsEntry("Keyword name cannot be empty.", DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR) + DiagnosticsEntry( + "Keyword name cannot be empty.", + DiagnosticSeverity.ERROR, + Error.KEYWORD_ERROR, + ) ) raise CancelSearchError if not isinstance(name, str): self.diagnostics.append( # type: ignore - DiagnosticsEntry("Keyword name must be a string.", DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR) + DiagnosticsEntry( + "Keyword name must be a string.", + DiagnosticSeverity.ERROR, + Error.KEYWORD_ERROR, + ) ) raise CancelSearchError @@ -1754,13 +1886,7 @@ def _get_keyword_from_self(self, name: str) -> Optional[KeywordDoc]: try: return self.self_library_doc.keywords.get(name, None) except KeywordError as e: - self.diagnostics.append( - DiagnosticsEntry( - str(e), - DiagnosticSeverity.ERROR, - Error.KEYWORD_ERROR, - ) - ) + self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR)) raise CancelSearchError from e def _yield_owner_and_kw_names(self, full_name: str) -> Iterator[Tuple[str, ...]]: @@ -1790,7 +1916,12 @@ def _get_explicit_keyword(self, name: str) -> Optional[KeywordDoc]: def find_keywords(self, owner_name: str, name: str) -> List[Tuple[LibraryEntry, KeywordDoc]]: if self._all_keywords is None: - self._all_keywords = list(chain(self.namespace._libraries.values(), self.namespace._resources.values())) + self._all_keywords = list( + chain( + self.namespace._libraries.values(), + self.namespace._resources.values(), + ) + ) if get_robot_version() >= (6, 0): result: List[Tuple[LibraryEntry, KeywordDoc]] = [] @@ -1814,7 +1945,10 @@ def _add_to_multiple_keywords_result(self, kw: Iterable[KeywordDoc]) -> None: self.multiple_keywords_result.extend(kw) def _create_multiple_keywords_found_message( - self, name: str, found: Sequence[Tuple[Optional[LibraryEntry], KeywordDoc]], implicit: bool = True + self, + name: str, + found: Sequence[Tuple[Optional[LibraryEntry], KeywordDoc]], + implicit: bool = True, ) -> str: self._add_to_multiple_keywords_result([k for _, k in found]) @@ -1871,7 +2005,9 @@ def _is_worse_match_than_others( return False def _is_better_match( - self, candidate: Tuple[Optional[LibraryEntry], KeywordDoc], other: Tuple[Optional[LibraryEntry], KeywordDoc] + self, + candidate: Tuple[Optional[LibraryEntry], KeywordDoc], + other: Tuple[Optional[LibraryEntry], KeywordDoc], ) -> bool: return ( other[1].matcher.embedded_arguments.match(candidate[1].name) is not None @@ -1979,7 +2115,9 @@ def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]: raise CancelSearchError def _filter_stdlib_runner( - self, entry1: Tuple[Optional[LibraryEntry], KeywordDoc], entry2: Tuple[Optional[LibraryEntry], KeywordDoc] + self, + entry1: Tuple[Optional[LibraryEntry], KeywordDoc], + entry2: Tuple[Optional[LibraryEntry], KeywordDoc], ) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]: stdlibs_without_remote = STDLIBS - {"Remote"} if entry1[0] is not None and entry1[0].name in stdlibs_without_remote: @@ -2000,7 +2138,9 @@ def _filter_stdlib_runner( return [custom] def _create_custom_and_standard_keyword_conflict_warning_message( - self, custom: Tuple[Optional[LibraryEntry], KeywordDoc], standard: Tuple[Optional[LibraryEntry], KeywordDoc] + self, + custom: Tuple[Optional[LibraryEntry], KeywordDoc], + standard: Tuple[Optional[LibraryEntry], KeywordDoc], ) -> str: custom_with_name = standard_with_name = "" if custom[0] is not None and custom[0].alias is not None: 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 cdab8538e..c481d48e1 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 @@ -19,7 +19,13 @@ from robot.parsing.lexer.tokens import Token from robotcode.core.concurrent import threaded -from robotcode.core.lsp.types import CodeAction, CodeActionContext, CodeActionKind, Command, Range +from robotcode.core.lsp.types import ( + CodeAction, + CodeActionContext, + CodeActionKind, + Command, + Range, +) from robotcode.core.uri import Uri from robotcode.core.utils.dataclasses import CamelSnakeMixin from robotcode.core.utils.logging import LoggingDescriptor @@ -41,9 +47,7 @@ from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: - from ..protocol import ( - RobotLanguageServerProtocol, - ) + from ..protocol import RobotLanguageServerProtocol @dataclass(repr=False) @@ -237,11 +241,7 @@ def _ensure_http_server_started(self) -> None: self._server_thread.start() @language_id("robotframework") - @code_action_kinds( - [ - CodeActionKind.SOURCE, - ] - ) + @code_action_kinds([CodeActionKind.SOURCE]) @_logger.call def collect( self, @@ -271,7 +271,10 @@ def collect( node.get_token(RobotToken.NAME) ): url = self.build_url( - node.name, node.args if isinstance(node, LibraryImport) else (), document, namespace + node.name, + node.args if isinstance(node, LibraryImport) else (), + document, + namespace, ) return [self.open_documentation_code_action(url)] @@ -335,14 +338,26 @@ def collect( if entry is None: return None - url = self.build_url(entry.import_name, entry.args, document, namespace, kw_doc.name) + url = self.build_url( + entry.import_name, + entry.args, + document, + namespace, + kw_doc.name, + ) return [self.open_documentation_code_action(url)] if isinstance(node, KeywordName): name_token = node.get_token(RobotToken.KEYWORD_NAME) if name_token is not None and range in range_from_token(name_token): - url = self.build_url(str(document.uri.to_path().name), (), document, namespace, name_token.value) + url = self.build_url( + str(document.uri.to_path().name), + (), + document, + namespace, + name_token.value, + ) return [self.open_documentation_code_action(url)] @@ -352,11 +367,7 @@ def open_documentation_code_action(self, url: str) -> CodeAction: return CodeAction( "Open Documentation", kind=CodeActionKind.SOURCE, - command=Command( - "Open Documentation", - "robotcode.showDocumentation", - [url], - ), + command=Command("Open Documentation", "robotcode.showDocumentation", [url]), ) def build_url( diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py index c942f37b5..7f93e4c0e 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py @@ -4,11 +4,7 @@ from dataclasses import dataclass from typing import List, Optional, Tuple -from robotcode.core.lsp.types import ( - DocumentUri, - Position, - Range, -) +from robotcode.core.lsp.types import DocumentUri, Position, Range from robotcode.robot.utils.ast import range_from_node from robotcode.robot.utils.visitor import Visitor @@ -63,7 +59,11 @@ def find_keyword_sections(node: ast.AST) -> Optional[List[ast.AST]]: class CodeActionHelperMixin: def create_insert_keyword_workspace_edit( - self, document: TextDocument, model: ast.AST, namespace: Namespace, insert_text: str + self, + document: TextDocument, + model: ast.AST, + namespace: Namespace, + insert_text: str, ) -> Tuple[str, Range]: keyword_sections = find_keyword_sections(model) keyword_section = keyword_sections[-1] if keyword_sections else None diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py index fc1da266f..82fc53cbc 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py @@ -1,7 +1,16 @@ from collections import defaultdict from dataclasses import dataclass from string import Template as StringTemplate -from typing import TYPE_CHECKING, Any, List, Mapping, Optional, Tuple, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + List, + Mapping, + Optional, + Tuple, + Union, + cast, +) from robot.parsing.lexer.tokens import Token from robot.parsing.model.blocks import Keyword, TestCase, VariableSection @@ -100,7 +109,11 @@ def __init__(self, parent: "RobotLanguageServerProtocol") -> None: @language_id("robotframework") @code_action_kinds([CodeActionKind.QUICK_FIX]) def collect( - self, sender: Any, document: TextDocument, range: Range, context: CodeActionContext + self, + sender: Any, + document: TextDocument, + range: Range, + context: CodeActionContext, ) -> Optional[List[Union[Command, CodeAction]]]: result = [] for method in iter_methods(self, lambda m: m.__name__.startswith("code_action_")): @@ -109,7 +122,7 @@ def collect( result.extend(code_actions) if result: - return list(sorted(result, key=lambda ca: ca.title)) + return sorted(result, key=lambda ca: ca.title) return None @@ -118,8 +131,16 @@ def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: type = code_action.data.get("type", None) if type == "quickfix": method_name = code_action.data.get("method") - method = next(iter_methods(self, lambda m: m.__name__ == f"resolve_code_action_{method_name}")) - method(code_action, data=from_dict(code_action.data, CodeActionData)) + method = next( + iter_methods( + self, + lambda m: m.__name__ == f"resolve_code_action_{method_name}", + ) + ) + method( + code_action, + data=from_dict(code_action.data, CodeActionData), + ) return None @@ -154,7 +175,10 @@ def code_action_create_keyword( if bdd_token is not None and token is not None: keyword_token = token - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(namespace, keyword_token) if lib_entry is not None and lib_entry.library_doc.type == "LIBRARY": disabled = CodeActionDisabledType("Keyword is from a library") @@ -172,7 +196,12 @@ def code_action_create_keyword( f"Create Keyword `{text}`", kind=CodeActionKind.QUICK_FIX, data=as_dict( - CodeActionData("quickfix", "create_keyword", document.document_uri, diagnostic.range) + CodeActionData( + "quickfix", + "create_keyword", + document.document_uri, + diagnostic.range, + ) ), diagnostics=[diagnostic], disabled=disabled, @@ -203,7 +232,10 @@ def resolve_code_action_create_keyword(self, code_action: CodeAction, data: Code if bdd_token is not None and token is not None: keyword_token = token - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(namespace, keyword_token) if lib_entry is not None and lib_entry.library_doc.type == "LIBRARY": return None @@ -223,7 +255,7 @@ def resolve_code_action_create_keyword(self, code_action: CodeAction, data: Code if value is not None and not contains_variable(name, "$@&%"): arguments.append(f"${{{name}}}") else: - arguments.append(f"${{arg{len(arguments)+1}}}") + arguments.append(f"${{arg{len(arguments) + 1}}}") insert_text = ( KEYWORD_WITH_ARGS_TEMPLATE.substitute(name=text, args=" ".join(arguments)) @@ -297,7 +329,11 @@ def code_action_disable_robotcode_diagnostics_for_line( kind=CodeActionKind.QUICK_FIX, data=as_dict( CodeActionData( - "quickfix", "disable_robotcode_diagnostics_for_line", document.document_uri, range, k + "quickfix", + "disable_robotcode_diagnostics_for_line", + document.document_uri, + range, + k, ) ), diagnostics=v, @@ -330,7 +366,13 @@ def resolve_code_action_disable_robotcode_diagnostics_for_line( document_changes=[ TextDocumentEdit( OptionalVersionedTextDocumentIdentifier(str(document.uri), document.version), - [AnnotatedTextEdit("disable_robotcode_diagnostics_for_line", insert_range, insert_text)], + [ + AnnotatedTextEdit( + "disable_robotcode_diagnostics_for_line", + insert_range, + insert_text, + ) + ], ) ], change_annotations={ @@ -428,12 +470,21 @@ def resolve_code_action_create_local_variable( insert_text = f"{spaces}${{{text}}} Set Variable value\n" node_range = range_from_node(node) - insert_range = Range(start=Position(node_range.start.line, 0), end=Position(node_range.start.line, 0)) + insert_range = Range( + start=Position(node_range.start.line, 0), + end=Position(node_range.start.line, 0), + ) code_action.edit = WorkspaceEdit( document_changes=[ TextDocumentEdit( OptionalVersionedTextDocumentIdentifier(str(document.uri), document.version), - [AnnotatedTextEdit("create_local_variable", insert_range, insert_text)], + [ + AnnotatedTextEdit( + "create_local_variable", + insert_range, + insert_text, + ) + ], ) ], change_annotations={"create_local_variable": ChangeAnnotation("Create Local variable", False)}, @@ -514,7 +565,10 @@ def resolve_code_action_create_suite_variable( if any(n for n in nodes if isinstance(n, (VariableSection))) and isinstance(node, Variable): node_range = range_from_node(node) - insert_range = Range(start=Position(node_range.start.line, 0), end=Position(node_range.start.line, 0)) + insert_range = Range( + start=Position(node_range.start.line, 0), + end=Position(node_range.start.line, 0), + ) else: finder = FindSectionsVisitor() finder.visit(model) @@ -527,7 +581,10 @@ def resolve_code_action_create_suite_variable( if end_lineno is None: return None - insert_range = Range(start=Position(end_lineno, 0), end=Position(end_lineno, 0)) + insert_range = Range( + start=Position(end_lineno, 0), + end=Position(end_lineno, 0), + ) else: insert_range_prefix = "\n\n*** Variables ***\n" if finder.setting_sections: @@ -535,12 +592,18 @@ def resolve_code_action_create_suite_variable( insert_range_suffix = "\n\n" section = finder.setting_sections[-1] - _, last_stmt = FirstAndLastRealStatementFinder.find_from(section) + ( + _, + last_stmt, + ) = FirstAndLastRealStatementFinder.find_from(section) end_lineno = last_stmt.end_lineno if last_stmt else section.end_lineno if end_lineno is None: return None - insert_range = Range(start=Position(end_lineno, 0), end=Position(end_lineno, 0)) + insert_range = Range( + start=Position(end_lineno, 0), + end=Position(end_lineno, 0), + ) else: insert_range_prefix = "*** Variables ***\n" insert_range_suffix = "\n\n" @@ -561,7 +624,13 @@ def resolve_code_action_create_suite_variable( document_changes=[ TextDocumentEdit( OptionalVersionedTextDocumentIdentifier(str(document.uri), document.version), - [AnnotatedTextEdit("create_suite_variable", insert_range, insert_text)], + [ + AnnotatedTextEdit( + "create_suite_variable", + insert_range, + insert_text, + ) + ], ) ], change_annotations={"create_suite_variable": ChangeAnnotation("Create suite variable", False)}, @@ -672,7 +741,10 @@ def resolve_code_action_add_argument(self, code_action: CodeAction, data: CodeAc insert_text = f"{spaces}[Arguments] ${{{text}}}=\n" node_range = range_from_node(first_stmt) - insert_range = Range(start=Position(node_range.start.line, 0), end=Position(node_range.start.line, 0)) + insert_range = Range( + start=Position(node_range.start.line, 0), + end=Position(node_range.start.line, 0), + ) else: insert_text = f" ${{{text}}}=" argument_tokens = arguments.get_tokens(Token.ARGUMENT) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_refactor.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_refactor.py index a04ef9d5f..1ea2f9713 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_refactor.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_refactor.py @@ -37,7 +37,11 @@ from ...common.decorators import code_action_kinds, language_id from ...common.text_document import TextDocument from ..diagnostics.model_helper import ModelHelperMixin -from .code_action_helper_mixin import SHOW_DOCUMENT_SELECT_AND_RENAME_COMMAND, CodeActionDataBase, CodeActionHelperMixin +from .code_action_helper_mixin import ( + SHOW_DOCUMENT_SELECT_AND_RENAME_COMMAND, + CodeActionDataBase, + CodeActionHelperMixin, +) from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: @@ -104,9 +108,18 @@ def __init__(self, parent: "RobotLanguageServerProtocol") -> None: self.parent.commands.register_all(self) @language_id("robotframework") - @code_action_kinds([CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION, CODE_ACTION_KIND_SURROUND_WITH]) + @code_action_kinds( + [ + CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION, + CODE_ACTION_KIND_SURROUND_WITH, + ] + ) def collect( - self, sender: Any, document: TextDocument, range: Range, context: CodeActionContext + self, + sender: Any, + document: TextDocument, + range: Range, + context: CodeActionContext, ) -> Optional[List[Union[Command, CodeAction]]]: result = [] for method in iter_methods(self, lambda m: m.__name__.startswith("code_action_")): @@ -115,7 +128,7 @@ def collect( result.extend(code_actions) if result: - return list(sorted(result, key=lambda ca: ca.title)) + return sorted(result, key=lambda ca: ca.title) return None @@ -124,8 +137,16 @@ def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: type = code_action.data.get("type", None) if type == "refactor": method_name = code_action.data.get("method") - method = next(iter_methods(self, lambda m: m.__name__ == f"resolve_code_action_{method_name}")) - method(code_action, data=from_dict(code_action.data, CodeActionData)) + method = next( + iter_methods( + self, + lambda m: m.__name__ == f"resolve_code_action_{method_name}", + ) + ) + method( + code_action, + data=from_dict(code_action.data, CodeActionData), + ) return None @@ -224,16 +245,7 @@ def get_valid_nodes_in_range(self, model: ast.AST, range: Range, also_return: bo if any( n for n in result - if isinstance( - n, - ( - IfHeader, - ElseIfHeader, - ElseHeader, - ForHeader, - End, - ), - ) + if isinstance(n, (IfHeader, ElseIfHeader, ElseHeader, ForHeader, End)) or get_robot_version() >= (5, 0) and isinstance(n, (WhileHeader, TryHeader, ExceptHeader, FinallyHeader)) ): @@ -287,7 +299,13 @@ def code_action_surround( "Surround with TRY...EXCEPT", kind=CODE_ACTION_KIND_SURROUND_WITH, data=as_dict( - CodeActionData("refactor", "surround", document.document_uri, insert_range, SurroundType.TRY_EXCEPT) + CodeActionData( + "refactor", + "surround", + document.document_uri, + insert_range, + SurroundType.TRY_EXCEPT, + ) ) if insert_range else None, @@ -298,7 +316,11 @@ def code_action_surround( kind=CODE_ACTION_KIND_SURROUND_WITH, data=as_dict( CodeActionData( - "refactor", "surround", document.document_uri, insert_range, SurroundType.TRY_FINALLY + "refactor", + "surround", + document.document_uri, + insert_range, + SurroundType.TRY_FINALLY, ) ) if insert_range @@ -310,7 +332,11 @@ def code_action_surround( kind=CODE_ACTION_KIND_SURROUND_WITH, data=as_dict( CodeActionData( - "refactor", "surround", document.document_uri, insert_range, SurroundType.TRY_EXCEPT_FINALLY + "refactor", + "surround", + document.document_uri, + insert_range, + SurroundType.TRY_EXCEPT_FINALLY, ) ) if insert_range @@ -377,9 +403,7 @@ def resolve_code_action_surround(self, code_action: CodeAction, data: CodeAction ], ) ], - change_annotations={ - "surround": ChangeAnnotation("surround", False), - }, + change_annotations={"surround": ChangeAnnotation("surround", False)}, ) code_action.command = Command( @@ -403,11 +427,7 @@ def code_action_assign_result_to_variable( if range.start.line == range.end.line and ( (context.only and CodeActionKind.REFACTOR_EXTRACT in context.only) - or context.trigger_kind - in [ - CodeActionTriggerKind.INVOKED, - CodeActionTriggerKind.AUTOMATIC, - ] + or context.trigger_kind in [CodeActionTriggerKind.INVOKED, CodeActionTriggerKind.AUTOMATIC] ): model = self.parent.documents_cache.get_model(document, False) node = get_node_at_position(model, range.start) @@ -428,7 +448,14 @@ def code_action_assign_result_to_variable( CodeAction( "Assign keyword result to variable", kind=CODE_ACTION_KIND_REFACTOR_EXTRACT_VARIABLE, - data=as_dict(CodeActionData("refactor", "assign_result_to_variable", document.document_uri, range)), + data=as_dict( + CodeActionData( + "refactor", + "assign_result_to_variable", + document.document_uri, + range, + ) + ), ) ] @@ -489,7 +516,13 @@ def resolve_code_action_assign_result_to_variable( document_changes=[ TextDocumentEdit( OptionalVersionedTextDocumentIdentifier(str(document.uri), document.version), - [AnnotatedTextEdit("assign_result_to_variable", Range(start, start), f"${{{var_name}}} ")], + [ + AnnotatedTextEdit( + "assign_result_to_variable", + Range(start, start), + f"${{{var_name}}} ", + ) + ], ) ], change_annotations={"assign_result_to_variable": ChangeAnnotation("Assign result to variable", False)}, @@ -536,11 +569,18 @@ def code_action_extract_keyword( CodeAction( "Extract keyword", kind=CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION, - data=as_dict(CodeActionData("refactor", "extract_keyword", document.document_uri, insert_range)) + data=as_dict( + CodeActionData( + "refactor", + "extract_keyword", + document.document_uri, + insert_range, + ) + ) if insert_range else None, disabled=disabled, - ), + ) ] def resolve_code_action_extract_keyword( @@ -611,7 +651,7 @@ def resolve_code_action_extract_keyword( ) } - argument_variables_text = " ".join(n.name for n in argument_variables.keys()) + argument_variables_text = " ".join(n.name for n in argument_variables) keyword_text = f"{keyword_name}\n" if argument_variables_text: keyword_text += f" [Arguments] {argument_variables_text}\n" @@ -638,7 +678,7 @@ def resolve_code_action_extract_keyword( keyword_text += " " + l if assigned_variables: - keyword_text += "\n RETURN " + " ".join(n.name for n in assigned_variables.keys()) + keyword_text += "\n RETURN " + " ".join(n.name for n in assigned_variables) keyword_text, keyword_range = self.create_insert_keyword_workspace_edit( document, model, namespace, keyword_text @@ -646,7 +686,7 @@ def resolve_code_action_extract_keyword( assigned_variables_text = "" if assigned_variables: - assigned_variables_text += " ".join(n.name for n in assigned_variables.keys()) + " " + assigned_variables_text += " ".join(n.name for n in assigned_variables) + " " keyword_call_text = f"{spaces}{assigned_variables_text}{keyword_name}" if argument_variables_text: @@ -676,8 +716,14 @@ def resolve_code_action_extract_keyword( [ document.document_uri, Range( - Position(data.range.start.line, len(spaces) + len(assigned_variables_text)), - Position(data.range.start.line, len(spaces) + len(assigned_variables_text) + len(keyword_name)), + Position( + data.range.start.line, + len(spaces) + len(assigned_variables_text), + ), + Position( + data.range.start.line, + len(spaces) + len(assigned_variables_text) + len(keyword_name), + ), ), ], ) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_lens.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_lens.py index 7e2a3ee30..f14ab6dd8 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/code_lens.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/code_lens.py @@ -75,7 +75,7 @@ def resolve(self, sender: Any, code_lens: CodeLens) -> Optional[CodeLens]: def find_refs() -> None: if document is None or kw_doc is None: - return # type: ignore[unreachable] + return # type: ignore[unreachable] self.parent.robot_references.find_keyword_references( document, kw_doc, include_declaration=False @@ -132,7 +132,10 @@ def visit(self, node: ast.AST) -> None: @classmethod def find_from( - cls, model: ast.AST, parent: RobotCodeLensProtocolPart, document: TextDocument + cls, + model: ast.AST, + parent: RobotCodeLensProtocolPart, + document: TextDocument, ) -> Optional[List[CodeLens]]: finder = cls(parent, document) 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 5ff74dd4c..5096076f2 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 @@ -60,7 +60,12 @@ ) 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.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, @@ -81,7 +86,9 @@ if get_robot_version() >= (6, 1): from robot.parsing.lexer.settings import SuiteFileSettings else: - from robot.parsing.lexer.settings import TestCaseFileSettings as SuiteFileSettings + from robot.parsing.lexer.settings import ( + TestCaseFileSettings as SuiteFileSettings, + ) if TYPE_CHECKING: from ..protocol import RobotLanguageServerProtocol @@ -129,13 +136,17 @@ def get_header_style(self, config: CompletionConfig) -> str: "{", "=", os.sep, - ], + ] ) # @all_commit_characters(['\n']) @language_id("robotframework") @_logger.call def collect( - self, sender: Any, document: TextDocument, position: Position, context: Optional[CompletionContext] + self, + sender: Any, + document: TextDocument, + position: Position, + context: Optional[CompletionContext], ) -> Union[List[CompletionItem], CompletionList, None]: namespace = self.parent.documents_cache.get_namespace(document) model = self.parent.documents_cache.get_model(document, False) @@ -143,11 +154,13 @@ def collect( config = self.get_config(document) return CompletionCollector( - self.parent, document, model, namespace, self.get_header_style(config), config - ).collect( - position, - context, - ) + self.parent, + document, + model, + namespace, + self.get_header_style(config), + config, + ).collect(position, context) @language_id("robotframework") @_logger.call @@ -163,7 +176,12 @@ def resolve(self, sender: Any, completion_item: CompletionItem) -> CompletionIte config = self.get_config(document) return CompletionCollector( - self.parent, document, model, namespace, self.get_header_style(config), config + self.parent, + document, + model, + namespace, + self.get_header_style(config), + config, ).resolve(completion_item) return completion_item @@ -185,15 +203,34 @@ def get_snippets() -> Dict[str, List[str]]: global __snippets if __snippets is None: __snippets = { - "FOR": [r"FOR \${${1}} ${2|IN,IN ENUMERATE,IN RANGE,IN ZIP|} ${3:arg}", "$0", "END", ""], + "FOR": [ + r"FOR \${${1}} ${2|IN,IN ENUMERATE,IN RANGE,IN ZIP|} ${3:arg}", + "$0", + "END", + "", + ], "IF": [r"IF \${${1}}", " $0", "END", ""], } if get_robot_version() >= (5, 0): __snippets.update( { - "TRYEX": ["TRY", " $0", r"EXCEPT message", " ", "END", ""], - "TRYEXAS": ["TRY", " $0", r"EXCEPT message AS \${ex}", " ", "END", ""], + "TRYEX": [ + "TRY", + " $0", + r"EXCEPT message", + " ", + "END", + "", + ], + "TRYEXAS": [ + "TRY", + " $0", + r"EXCEPT message AS \${ex}", + " ", + "END", + "", + ], "WHILE": [r"WHILE ${1:expression}", " $0", "END", ""], } ) @@ -215,14 +252,7 @@ def get_reserved_keywords() -> List[str]: global __reserved_keywords if __reserved_keywords is None: - __reserved_keywords = [ - "FOR", - "END", - "IF", - "ELSE", - "ELIF", - "ELSE IF", - ] + __reserved_keywords = ["FOR", "END", "IF", "ELSE", "ELIF", "ELSE IF"] if get_robot_version() >= (5, 0): __reserved_keywords += [ "TRY", @@ -234,9 +264,7 @@ def get_reserved_keywords() -> List[str]: "RETURN", ] if get_robot_version() >= (7, 0): - __reserved_keywords += [ - "VAR", - ] + __reserved_keywords += ["VAR"] __reserved_keywords = sorted(__reserved_keywords) return __reserved_keywords @@ -362,14 +390,20 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: ) if lib_doc is not None: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=lib_doc.to_markdown(False) + kind=MarkupKind.MARKDOWN, + value=lib_doc.to_markdown(False), ) - except (SystemExit, KeyboardInterrupt, CancelledError): + except ( + SystemExit, + KeyboardInterrupt, + CancelledError, + ): raise except BaseException as e: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=f"Error:\n{e}" + kind=MarkupKind.MARKDOWN, + value=f"Error:\n{e}", ) elif (name := data.get("name", None)) is not None: try: @@ -382,7 +416,8 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: if lib_doc is not None: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=lib_doc.to_markdown(False) + kind=MarkupKind.MARKDOWN, + value=lib_doc.to_markdown(False), ) except (SystemExit, KeyboardInterrupt): @@ -390,7 +425,7 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: except BaseException: pass - elif comp_type in [CompleteResultKind.RESOURCE.name]: + elif comp_type == CompleteResultKind.RESOURCE.name: if (res_id := data.get("id", None)) is not None: try: lib_doc = next( @@ -404,14 +439,20 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: if lib_doc is not None: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=lib_doc.to_markdown(False) + kind=MarkupKind.MARKDOWN, + value=lib_doc.to_markdown(False), ) - except (SystemExit, KeyboardInterrupt, CancelledError): + except ( + SystemExit, + KeyboardInterrupt, + CancelledError, + ): raise except BaseException as e: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=f"Error:\n{e}" + kind=MarkupKind.MARKDOWN, + value=f"Error:\n{e}", ) elif (name := data.get("name", None)) is not None: @@ -424,14 +465,15 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: if lib_doc is not None: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=lib_doc.to_markdown(False) + kind=MarkupKind.MARKDOWN, + value=lib_doc.to_markdown(False), ) except (SystemExit, KeyboardInterrupt): raise except BaseException: pass - elif comp_type in [CompleteResultKind.KEYWORD.name]: + elif comp_type == CompleteResultKind.KEYWORD.name: kw_id = data.get("id", None) if kw_id is not None: try: @@ -442,10 +484,15 @@ def resolve(self, completion_item: CompletionItem) -> CompletionItem: if kw_doc is not None: completion_item.documentation = MarkupContent( - kind=MarkupKind.MARKDOWN, value=kw_doc.to_markdown() + kind=MarkupKind.MARKDOWN, + value=kw_doc.to_markdown(), ) - except (SystemExit, KeyboardInterrupt, CancelledError): + except ( + SystemExit, + KeyboardInterrupt, + CancelledError, + ): raise except BaseException: pass @@ -495,12 +542,7 @@ def create_headers_completion_items(self, range: Optional[Range]) -> List[Comple else None, sort_text=f"100_{s[1]}", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=range, - new_text=s[0], - ) - if range is not None - else None, + text_edit=TextEdit(range=range, new_text=s[0]) if range is not None else None, ) for s in ((self.header_style.format(name=k), k) for k in (v.title() for v in headers)) ] @@ -513,12 +555,7 @@ def create_environment_variables_completion_items(self, range: Optional[Range]) detail="Variable", sort_text=f"035_{s}", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=range, - new_text=s, - ) - if range is not None - else None, + text_edit=TextEdit(range=range, new_text=s) if range is not None else None, ) for s in self.namespace.imports_manager.environment.keys() ] @@ -543,10 +580,7 @@ def create_variables_completion_items( detail=f"{s.type.value}", sort_text=f"{self._VARIABLE_COMPLETION_SORT_TEXT_PREFIX.get(s.type, '035')}_{s.name[2:-1]}", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=range, - new_text=s.name[2:-1], - ), + text_edit=TextEdit(range=range, new_text=s.name[2:-1]), filter_text=s.name[2:-1] if range is not None else None, ) for s in (self.namespace.get_variable_matchers(list(reversed(nodes)), position)).values() @@ -638,7 +672,10 @@ def create_testcase_settings_completion_items(self, range: Optional[Range]) -> L ] def create_bdd_prefix_completion_items( - self, range: Optional[Range], at_top: bool = False, with_space: bool = True + self, + range: Optional[Range], + at_top: bool = False, + with_space: bool = True, ) -> List[CompletionItem]: prefixes = {"Given", "When", "Then", "And", "But"} @@ -1085,7 +1122,7 @@ def complete_default( return None token_at_position_index = tokens_at_position.index(token_at_position) - while token_at_position.type in [Token.EOL]: + while token_at_position.type == Token.EOL: token_at_position_index -= 1 if token_at_position_index < 0: break @@ -1112,7 +1149,10 @@ def complete_default( variable_end = token_at_position.value.find("}", open_brace_index + 1) contains_spezial = any( a - for a in itertools.takewhile(lambda b: b != "}", token_at_position.value[open_brace_index + 1 :]) + for a in itertools.takewhile( + lambda b: b != "}", + token_at_position.value[open_brace_index + 1 :], + ) if a in "+-*/" ) range = Range( @@ -1240,13 +1280,7 @@ def _complete_TestCase_or_Keyword( # noqa: N802 r.end.character += 1 if position.is_in_range(r): - return create_items( - in_assign, - in_template, - r, - token, - position, - ) + return create_items(in_assign, in_template, r, token, position) return None @@ -1258,7 +1292,11 @@ def complete_TestCase( # noqa: N802 context: Optional[CompletionContext], ) -> Union[List[CompletionItem], CompletionList, None]: def create_items( - in_assign: bool, in_template: bool, r: Optional[Range], token: Optional[Token], pos: Position + in_assign: bool, + in_template: bool, + r: Optional[Range], + token: Optional[Token], + pos: Position, ) -> Union[List[CompletionItem], CompletionList, None]: return [ e @@ -1299,7 +1337,14 @@ def check_in_template() -> bool: in_template = check_in_template() - return self._complete_TestCase_or_Keyword(node, nodes_at_position, position, context, in_template, create_items) + return self._complete_TestCase_or_Keyword( + node, + nodes_at_position, + position, + context, + in_template, + create_items, + ) def complete_Keyword( # noqa: N802 self, @@ -1309,7 +1354,11 @@ def complete_Keyword( # noqa: N802 context: Optional[CompletionContext], ) -> Union[List[CompletionItem], CompletionList, None]: def create_items( - in_assign: bool, in_template: bool, r: Optional[Range], token: Optional[Token], pos: Position + in_assign: bool, + in_template: bool, + r: Optional[Range], + token: Optional[Token], + pos: Position, ) -> Union[List[CompletionItem], CompletionList, None]: return [ e @@ -1463,7 +1512,11 @@ def complete_Setup_or_Teardown_or_Template( # noqa: N802 r = range_from_token(token) if position.is_in_range(r): return self.create_keyword_completion_items( - token, position, add_reserverd=False, add_none=True, in_template=isinstance(node, Template) + token, + position, + add_reserverd=False, + add_none=True, + in_template=isinstance(node, Template), ) if len(statement_node.tokens) > 4: @@ -1613,11 +1666,15 @@ def complete_import() -> Optional[List[CompletionItem]]: CompletionItem( label=e.label, kind=CompletionItemKind.MODULE - if e.kind in [CompleteResultKind.MODULE, CompleteResultKind.MODULE_INTERNAL] + if e.kind + in [ + CompleteResultKind.MODULE, + CompleteResultKind.MODULE_INTERNAL, + ] else CompletionItemKind.FILE - if e.kind in [CompleteResultKind.FILE] + if e.kind == CompleteResultKind.FILE else CompletionItemKind.FOLDER - if e.kind in [CompleteResultKind.FOLDER] + if e.kind == CompleteResultKind.FOLDER else None, detail=e.kind.value, sort_text=f"030_{e}", @@ -1638,7 +1695,10 @@ def complete_arguments() -> Optional[List[CompletionItem]]: ).end: return None - with_name_token = next((v for v in import_node.tokens if v.type == Token.WITH_NAME), None) + with_name_token = next( + (v for v in import_node.tokens if v.type == Token.WITH_NAME), + None, + ) if with_name_token is not None and position >= range_from_token(with_name_token).start: return None @@ -1664,7 +1724,10 @@ def complete_arguments() -> Optional[List[CompletionItem]]: if init: name_token_index = import_node.tokens.index(name_token) return self._complete_keyword_arguments_at_position( - init, kw_node.tokens[name_token_index:], token_at_position, position + init, + kw_node.tokens[name_token_index:], + token_at_position, + position, ) except (SystemExit, KeyboardInterrupt, CancelledError): @@ -1675,7 +1738,10 @@ def complete_arguments() -> Optional[List[CompletionItem]]: return None def complete_with_name() -> Optional[List[CompletionItem]]: - with_name_token = next((v for v in import_node.tokens if v.type == Token.WITH_NAME), None) + with_name_token = next( + (v for v in import_node.tokens if v.type == Token.WITH_NAME), + None, + ) if with_name_token is not None and position < range_from_token(with_name_token).start: return None @@ -1797,11 +1863,11 @@ def complete_ResourceImport( # noqa: N802 CompletionItem( label=e.label, kind=CompletionItemKind.FILE - if e.kind in [CompleteResultKind.RESOURCE] + if e.kind == CompleteResultKind.RESOURCE else CompletionItemKind.FILE - if e.kind in [CompleteResultKind.FILE] + if e.kind == CompleteResultKind.FILE else CompletionItemKind.FOLDER - if e.kind in [CompleteResultKind.FOLDER] + if e.kind == CompleteResultKind.FOLDER else None, detail=e.kind.value, sort_text=f"030_{e}", @@ -1917,11 +1983,11 @@ def complete_import() -> Optional[List[CompletionItem]]: CompletionItem( label=e.label, kind=CompletionItemKind.FILE - if e.kind in [CompleteResultKind.VARIABLES] + if e.kind == CompleteResultKind.VARIABLES else CompletionItemKind.FILE - if e.kind in [CompleteResultKind.FILE] + if e.kind == CompleteResultKind.FILE else CompletionItemKind.FOLDER - if e.kind in [CompleteResultKind.FOLDER] + if e.kind == CompleteResultKind.FOLDER else None, detail=e.kind.value, sort_text=f"030_{e}", @@ -1962,7 +2028,10 @@ def complete_arguments() -> Optional[List[CompletionItem]]: if init: name_token_index = import_node.tokens.index(name_token) return self._complete_keyword_arguments_at_position( - init, kw_node.tokens[name_token_index:], token_at_position, position + init, + kw_node.tokens[name_token_index:], + token_at_position, + position, ) except (SystemExit, KeyboardInterrupt, CancelledError): @@ -1999,7 +2068,11 @@ def _complete_KeywordCall_or_Fixture( # noqa: N802 token_at_position = tokens_at_position[-1] - if token_at_position.type not in [Token.ARGUMENT, Token.EOL, Token.SEPARATOR]: + if token_at_position.type not in [ + Token.ARGUMENT, + Token.EOL, + Token.SEPARATOR, + ]: return None if len(tokens_at_position) > 1 and tokens_at_position[-2].type == Token.KEYWORD: @@ -2031,21 +2104,30 @@ def _complete_KeywordCall_or_Fixture( # noqa: N802 keyword_token_index = kw_node.tokens.index(keyword_token) return self._complete_keyword_arguments_at_position( - keyword_doc, kw_node.tokens[keyword_token_index:], token_at_position, position + keyword_doc, + kw_node.tokens[keyword_token_index:], + token_at_position, + position, ) TRUE_STRINGS = {"TRUE", "YES", "ON", "1"} FALSE_STRINGS = {"FALSE", "NO", "OFF", "0", "NONE", ""} def _complete_keyword_arguments_at_position( - self, keyword_doc: KeywordDoc, tokens: Tuple[Token, ...], token_at_position: Token, position: Position + self, + keyword_doc: KeywordDoc, + tokens: Tuple[Token, ...], + token_at_position: Token, + position: Position, ) -> Optional[List[CompletionItem]]: if keyword_doc.is_any_run_keyword(): return None - argument_index, kw_arguments, argument_token = self.get_argument_info_at_position( - keyword_doc, tokens, token_at_position, position - ) + ( + argument_index, + kw_arguments, + argument_token, + ) = self.get_argument_info_at_position(keyword_doc, tokens, token_at_position, position) complete_argument_names = True complete_argument_values = True @@ -2137,7 +2219,10 @@ def _complete_keyword_arguments_at_position( label=b_snippet[0], kind=CompletionItemKind.CONSTANT, detail=f"{type_info.name}({b_snippet[1]})", - documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()), + documentation=MarkupContent( + MarkupKind.MARKDOWN, + type_info.to_markdown(), + ), sort_text=f"01_000_{int(not b_snippet[1])}_{b_snippet[0]}", insert_text_format=InsertTextFormat.PLAIN_TEXT, text_edit=TextEdit( @@ -2155,10 +2240,7 @@ def _complete_keyword_arguments_at_position( documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()), sort_text="50_000_None", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=completion_range, - new_text="None", - ), + text_edit=TextEdit(range=completion_range, new_text="None"), ) ) result.append( @@ -2169,10 +2251,7 @@ def _complete_keyword_arguments_at_position( documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()), sort_text="50_001_None", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=completion_range, - new_text="${None}", - ), + text_edit=TextEdit(range=completion_range, new_text="${None}"), ) ) if type_info.members: @@ -2188,10 +2267,7 @@ def _complete_keyword_arguments_at_position( ), sort_text=f"09_{i:03}_{member_index:03}_{member.name}", insert_text_format=InsertTextFormat.PLAIN_TEXT, - text_edit=TextEdit( - range=completion_range, - new_text=member.name, - ), + text_edit=TextEdit(range=completion_range, new_text=member.name), ) ) if type_info.items: @@ -2216,13 +2292,13 @@ def _complete_keyword_arguments_at_position( label=snippet, kind=CompletionItemKind.STRUCT, detail=type_info.name, - documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()), + documentation=MarkupContent( + MarkupKind.MARKDOWN, + type_info.to_markdown(), + ), sort_text=f"08_{i:03}_{snippet}", insert_text_format=InsertTextFormat.SNIPPET, - text_edit=TextEdit( - range=completion_range, - new_text=snippet, - ), + text_edit=TextEdit(range=completion_range, new_text=snippet), ) ) @@ -2230,7 +2306,7 @@ def _complete_keyword_arguments_at_position( known_names = [] if (argument_token or token_at_position).type == Token.ARGUMENT and position == range_from_token( - (argument_token or token_at_position) + argument_token or token_at_position ).start: completion_range = Range(position, position) @@ -2252,9 +2328,21 @@ def _complete_keyword_arguments_at_position( preselected = -1 if known_names and before_is_named: n = known_names[-1] - preselected = next((i for i, e in enumerate(kw_arguments) if e.name == n), -1) + 1 + preselected = ( + next( + (i for i, e in enumerate(kw_arguments) if e.name == n), + -1, + ) + + 1 + ) if preselected >= len(kw_arguments): - preselected = next((i for i, e in enumerate(kw_arguments) if e.name == n), -1) - 1 + preselected = ( + next( + (i for i, e in enumerate(kw_arguments) if e.name == n), + -1, + ) + - 1 + ) result += [ CompletionItem( diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/debugging_utils.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/debugging_utils.py index 6ae3a73db..508c601b5 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/debugging_utils.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/debugging_utils.py @@ -9,7 +9,11 @@ from robotcode.core.utils.dataclasses import CamelSnakeMixin from robotcode.core.utils.logging import LoggingDescriptor from robotcode.jsonrpc2.protocol import rpc_method -from robotcode.robot.utils.ast import get_nodes_at_position, get_tokens_at_position, range_from_token +from robotcode.robot.utils.ast import ( + get_nodes_at_position, + get_tokens_at_position, + range_from_token, +) from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart @@ -36,7 +40,10 @@ class RobotDebuggingUtilsProtocolPart(RobotLanguageServerProtocolPart, ModelHelp def __init__(self, parent: RobotLanguageServerProtocol) -> None: super().__init__(parent) - @rpc_method(name="robot/debugging/getEvaluatableExpression", param_type=EvaluatableExpressionParams) + @rpc_method( + name="robot/debugging/getEvaluatableExpression", + param_type=EvaluatableExpressionParams, + ) @threaded @_logger.call async def _get_evaluatable_expression( 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 f419498e5..c08316b64 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 @@ -4,11 +4,21 @@ from robot.parsing.lexer.tokens import Token from robotcode.core.concurrent import threaded -from robotcode.core.lsp.types import Diagnostic, DiagnosticSeverity, DiagnosticTag, Position, Range +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.ast import ( + iter_nodes, + range_from_node, + range_from_token, +) from robotcode.robot.utils.stubs import HasError, HasErrors, HeaderAndBodyBlock from ...common.decorators import language_id @@ -81,10 +91,7 @@ def _collect_namespace_diagnostics(self, document: TextDocument) -> DiagnosticsR [ Diagnostic( range=Range( - start=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), end=Position( line=len(document.get_lines()), character=len((document.get_lines())[-1] or ""), @@ -99,7 +106,11 @@ def _collect_namespace_diagnostics(self, document: TextDocument) -> DiagnosticsR ) def _create_error_from_node( - self, node: ast.AST, msg: str, source: Optional[str] = None, only_start: bool = True + self, + node: ast.AST, + msg: str, + source: Optional[str] = None, + only_start: bool = True, ) -> Diagnostic: from robot.parsing.model.statements import Statement @@ -140,9 +151,10 @@ def _collect_token_errors(self, document: TextDocument) -> DiagnosticsResult: result: List[Diagnostic] = [] try: for token in self.parent.documents_cache.get_tokens(document): - if token.type in [Token.ERROR, Token.FATAL_ERROR] and not Namespace.should_ignore( - document, range_from_token(token) - ): + if token.type in [ + Token.ERROR, + Token.FATAL_ERROR, + ] and not Namespace.should_ignore(document, range_from_token(token)): result.append(self._create_error_from_token(token)) try: @@ -175,10 +187,7 @@ def _collect_token_errors(self, document: TextDocument) -> DiagnosticsResult: [ Diagnostic( range=Range( - start=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), end=Position( line=len(document.get_lines()), character=len((document.get_lines())[-1] or ""), @@ -224,10 +233,7 @@ def _collect_model_errors(self, document: TextDocument) -> DiagnosticsResult: [ Diagnostic( range=Range( - start=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), end=Position( line=len(document.get_lines()), character=len((document.get_lines())[-1] or ""), @@ -282,10 +288,7 @@ def _collect_unused_keyword_references(self, document: TextDocument) -> Diagnost [ Diagnostic( range=Range( - start=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), end=Position( line=len(document.get_lines()), character=len((document.get_lines())[-1] or ""), @@ -342,10 +345,7 @@ def _collect_unused_variable_references(self, document: TextDocument) -> Diagnos [ Diagnostic( range=Range( - start=Position( - line=0, - character=0, - ), + start=Position(line=0, character=0), end=Position( line=len(document.get_lines()), character=len((document.get_lines())[-1] or ""), diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/document_highlight.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/document_highlight.py index 519a54e59..e3028a229 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/document_highlight.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/document_highlight.py @@ -1,13 +1,20 @@ from typing import TYPE_CHECKING, Any, List, Optional, cast from robotcode.core.concurrent import check_current_thread_canceled -from robotcode.core.lsp.types import DocumentHighlight, DocumentHighlightKind, Position, Range +from robotcode.core.lsp.types import ( + DocumentHighlight, + DocumentHighlightKind, + Position, + Range, +) from robotcode.core.utils.logging import LoggingDescriptor from robotcode.language_server.common.decorators import language_id from robotcode.language_server.common.text_document import TextDocument if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) from .protocol_part import RobotLanguageServerProtocolPart @@ -22,12 +29,7 @@ def __init__(self, parent: "RobotLanguageServerProtocol") -> None: @language_id("robotframework") @_logger.call - def collect( - self, - sender: Any, - document: TextDocument, - position: Position, - ) -> Optional[List[DocumentHighlight]]: + def collect(self, sender: Any, document: TextDocument, position: Position) -> Optional[List[DocumentHighlight]]: namespace = self.parent.documents_cache.get_namespace(document) all_variable_refs = namespace.get_variable_references() @@ -39,7 +41,12 @@ def collect( if (var.source == namespace.source and position in var.name_range) or position in r.range: return [ *( - [DocumentHighlight(var.name_range, DocumentHighlightKind.TEXT)] + [ + DocumentHighlight( + var.name_range, + DocumentHighlightKind.TEXT, + ) + ] if var.source == namespace.source else [] ), @@ -71,29 +78,23 @@ def collect( if ns.import_source == namespace.source and (position.is_in_range(ns.alias_range, False) or position.is_in_range(ns.import_range, False)) else cast( - Optional[Range], next((r.range for r in ns_refs if position.is_in_range(r.range, False)), None) + Optional[Range], + next( + (r.range for r in ns_refs if position.is_in_range(r.range, False)), + None, + ), ) ) if found_range is not None: return [ *( - [ - DocumentHighlight( - ns.import_range, - DocumentHighlightKind.TEXT, - ) - ] + [DocumentHighlight(ns.import_range, DocumentHighlightKind.TEXT)] if ns.import_source == namespace.source and ns.import_range else [] ), *( - [ - DocumentHighlight( - ns.alias_range, - DocumentHighlightKind.TEXT, - ) - ] + [DocumentHighlight(ns.alias_range, DocumentHighlightKind.TEXT)] if ns.import_source == namespace.source and ns.alias_range else [] ), diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/document_symbols.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/document_symbols.py index 164ce38b2..381583d19 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/document_symbols.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/document_symbols.py @@ -7,9 +7,17 @@ from robot.parsing.model.blocks import Keyword, Section, TestCase from robot.parsing.model.statements import Statement from robot.variables import search_variable -from robotcode.core.lsp.types import DocumentSymbol, SymbolInformation, SymbolKind +from robotcode.core.lsp.types import ( + DocumentSymbol, + SymbolInformation, + SymbolKind, +) from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.utils.ast import range_from_node, range_from_token, tokenize_variables +from robotcode.robot.utils.ast import ( + range_from_node, + range_from_token, + tokenize_variables, +) from robotcode.robot.utils.visitor import Visitor from ...common.decorators import language_id @@ -83,7 +91,13 @@ def visit_TestCase(self, node: TestCase) -> None: # noqa: N802 if self.current_symbol is not None and self.current_symbol.children is not None: r = range_from_node(node) - symbol = DocumentSymbol(name=node.name, kind=SymbolKind.METHOD, range=r, selection_range=r, children=[]) + symbol = DocumentSymbol( + name=node.name, + kind=SymbolKind.METHOD, + range=r, + selection_range=r, + children=[], + ) self.current_symbol.children.append(symbol) self.generic_visit_current_symbol(node, symbol) @@ -94,7 +108,13 @@ def visit_Keyword(self, node: Keyword) -> None: # noqa: N802 if self.current_symbol is not None and self.current_symbol.children is not None: r = range_from_node(node) - symbol = DocumentSymbol(name=node.name, kind=SymbolKind.FUNCTION, range=r, selection_range=r, children=[]) + symbol = DocumentSymbol( + name=node.name, + kind=SymbolKind.FUNCTION, + range=r, + selection_range=r, + children=[], + ) self.current_symbol.children.append(symbol) self.generic_visit_current_symbol(node, symbol) @@ -112,7 +132,12 @@ def visit_Arguments(self, node: Statement) -> None: # noqa: N802 if argument is not None: r = range_from_token(argument) - symbol = DocumentSymbol(name=argument.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r) + symbol = DocumentSymbol( + name=argument.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, + ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) @@ -144,7 +169,10 @@ def visit_KeywordCall(self, node: Statement) -> None: # noqa: N802 r = range_from_token(variable_token) symbol = DocumentSymbol( - name=variable_token.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r + name=variable_token.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) @@ -161,7 +189,10 @@ def visit_ForHeader(self, node: Statement) -> None: # noqa: N802 if variable_token is not None: r = range_from_token(variable_token) symbol = DocumentSymbol( - name=variable_token.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r + name=variable_token.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) @@ -175,7 +206,10 @@ def visit_ExceptHeader(self, node: Statement) -> None: # noqa: N802 if variable_token is not None: r = range_from_token(variable_token) symbol = DocumentSymbol( - name=variable_token.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r + name=variable_token.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) @@ -189,7 +223,10 @@ def visit_Var(self, node: Statement) -> None: # noqa: N802 if variable_token is not None: r = range_from_token(variable_token) symbol = DocumentSymbol( - name=variable_token.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r + name=variable_token.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) @@ -208,7 +245,10 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802 if variable_token is not None: r = range_from_token(variable_token) symbol = DocumentSymbol( - name=variable_token.value, kind=SymbolKind.VARIABLE, range=r, selection_range=r + name=variable_token.value, + kind=SymbolKind.VARIABLE, + range=r, + selection_range=r, ) if symbol.name not in map(lambda v: v.name, self.current_symbol.children): self.current_symbol.children.append(symbol) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/documents_cache.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/documents_cache.py index 4279fefea..3e28c5142 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/documents_cache.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/documents_cache.py @@ -58,7 +58,9 @@ def get_workspace_languages(self, document_or_uri: Union[TextDocument, Uri, str] if get_robot_version() < (6, 0): return None - from robot.conf.languages import Languages as RobotLanguages # pyright: ignore[reportMissingImports] + from robot.conf.languages import ( + Languages as RobotLanguages, + ) uri: Union[Uri, str] @@ -106,7 +108,11 @@ def build_languages_from_model( if get_robot_version() < (6, 0): return (None, None) - from robot.conf.languages import Languages as RobotLanguages # pyright: ignore[reportMissingImports] + from robot.conf.languages import ( + Languages as RobotLanguages, + ) + + # pyright: ignore[reportMissingImports] from robot.parsing.model.blocks import File workspace_langs = self.get_workspace_languages(document) @@ -173,12 +179,21 @@ def get_general_tokens(self, document: TextDocument, data_only: bool = False) -> return document.get_cache(self.__get_general_tokens) def __internal_get_tokens( - self, source: Any, data_only: bool = False, tokenize_variables: bool = False, lang: Any = None + self, + source: Any, + data_only: bool = False, + tokenize_variables: bool = False, + lang: Any = None, ) -> Any: import robot.api if get_robot_version() >= (6, 0): - return robot.api.get_tokens(source, data_only=data_only, tokenize_variables=tokenize_variables, lang=lang) + return robot.api.get_tokens( + source, + data_only=data_only, + tokenize_variables=tokenize_variables, + lang=lang, + ) return robot.api.get_tokens(source, data_only=data_only, tokenize_variables=tokenize_variables) @@ -193,19 +208,29 @@ def __internal_get_resource_tokens( if get_robot_version() >= (6, 0): return robot.api.get_resource_tokens( - source, data_only=data_only, tokenize_variables=tokenize_variables, lang=lang + source, + data_only=data_only, + tokenize_variables=tokenize_variables, + lang=lang, ) return robot.api.get_resource_tokens(source, data_only=data_only, tokenize_variables=tokenize_variables) def __internal_get_init_tokens( - self, source: Any, data_only: bool = False, tokenize_variables: bool = False, lang: Any = None + self, + source: Any, + data_only: bool = False, + tokenize_variables: bool = False, + lang: Any = None, ) -> Any: import robot.api if get_robot_version() >= (6, 0): return robot.api.get_init_tokens( - source, data_only=data_only, tokenize_variables=tokenize_variables, lang=lang + source, + data_only=data_only, + tokenize_variables=tokenize_variables, + lang=lang, ) return robot.api.get_init_tokens(source, data_only=data_only, tokenize_variables=tokenize_variables) @@ -228,11 +253,7 @@ def get(text: str) -> List[Token]: return self.__get_tokens_internal(document, get) - def __get_tokens_internal( - self, - document: TextDocument, - get: Callable[[str], List[Token]], - ) -> List[Token]: + def __get_tokens_internal(self, document: TextDocument, get: Callable[[str], List[Token]]) -> List[Token]: return get(document.text()) def get_resource_tokens(self, document: TextDocument, data_only: bool = False) -> List[Token]: @@ -294,7 +315,12 @@ def get_model(self, document: TextDocument, data_only: bool = True) -> ast.AST: raise UnknownFileTypeError(f"Unknown file type '{document.uri}'.") - def __get_model(self, document: TextDocument, tokens: Iterable[Any], document_type: DocumentType) -> ast.AST: + def __get_model( + self, + document: TextDocument, + tokens: Iterable[Any], + document_type: DocumentType, + ) -> ast.AST: from robot.parsing.parser.parser import _get_model def get_tokens(source: str, data_only: bool = False, lang: Any = None) -> Iterator[Token]: @@ -306,14 +332,17 @@ def get_tokens(source: str, data_only: bool = False, lang: Any = None) -> Iterat else: model = _get_model(get_tokens, document.uri.to_path(), False, None) - setattr(model, "source", str(document.uri.to_path())) - setattr(model, "model_type", document_type) + model.source = str(document.uri.to_path()) + model.model_type = document_type return cast(ast.AST, model) def get_general_model(self, document: TextDocument, data_only: bool = True) -> ast.AST: if data_only: - return document.get_cache(self.__get_general_model_data_only, self.get_general_tokens(document, True)) + return document.get_cache( + self.__get_general_model_data_only, + self.get_general_tokens(document, True), + ) return document.get_cache(self.__get_general_model, self.get_general_tokens(document)) def __get_general_model_data_only(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST: @@ -324,7 +353,10 @@ def __get_general_model(self, document: TextDocument, tokens: Iterable[Any]) -> def get_resource_model(self, document: TextDocument, data_only: bool = True) -> ast.AST: if data_only: - return document.get_cache(self.__get_resource_model_data_only, self.get_resource_tokens(document, True)) + return document.get_cache( + self.__get_resource_model_data_only, + self.get_resource_tokens(document, True), + ) return document.get_cache(self.__get_resource_model, self.get_resource_tokens(document)) @@ -336,7 +368,10 @@ def __get_resource_model(self, document: TextDocument, tokens: Iterable[Any]) -> def get_init_model(self, document: TextDocument, data_only: bool = True) -> ast.AST: if data_only: - return document.get_cache(self.__get_init_model_data_only, self.get_init_tokens(document, True)) + return document.get_cache( + self.__get_init_model_data_only, + self.get_init_tokens(document, True), + ) return document.get_cache(self.__get_init_model, self.get_init_tokens(document)) def __get_init_model_data_only(self, document: TextDocument, tokens: Iterable[Any]) -> ast.AST: @@ -378,25 +413,15 @@ def __invalidate_namespace(self, sender: Namespace) -> None: if document is not None: document.invalidate_cache() - self.namespace_invalidated( - self, - sender, - callback_filter=language_id_filter(document), - ) + self.namespace_invalidated(self, sender, callback_filter=language_id_filter(document)) def __document_cache_invalidated(self, sender: TextDocument) -> None: namespace: Optional[Namespace] = sender.get_cache_value(self.__get_namespace) if namespace is not None: - self.namespace_invalidated( - self, - namespace, - callback_filter=language_id_filter(sender), - ) + self.namespace_invalidated(self, namespace, callback_filter=language_id_filter(sender)) def __get_namespace_for_document_type( - self, - document: TextDocument, - document_type: Optional[DocumentType], + self, document: TextDocument, document_type: Optional[DocumentType] ) -> Namespace: if document_type is not None and document_type == DocumentType.INIT: model = self.get_init_model(document) @@ -412,7 +437,13 @@ def __get_namespace_for_document_type( languages, workspace_languages = self.build_languages_from_model(document, model) result = Namespace( - imports_manager, model, str(document.uri.to_path()), document, document_type, languages, workspace_languages + imports_manager, + model, + str(document.uri.to_path()), + document, + document_type, + languages, + workspace_languages, ) result.has_invalidated.add(self.__invalidate_namespace) result.has_imports_changed.add(self.__invalidate_namespace) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/folding_range.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/folding_range.py index 4a037ef27..5026b4ac6 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/folding_range.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/folding_range.py @@ -102,7 +102,11 @@ def visit_If(self, node: If) -> None: # noqa: N802 if node.orelse is not None and node.body[-1]: self.__append(node, kind="if", end_node=node.body[-1]) elif node.orelse is None and node.type == "ELSE": - self.__append(node, kind="if", end_node=self.current_if[-1] if self.current_if else None) + self.__append( + node, + kind="if", + end_node=self.current_if[-1] if self.current_if else None, + ) else: self.__append(node, kind="if") diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/formatting.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/formatting.py index ca56f90f1..c872291a5 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/formatting.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/formatting.py @@ -22,7 +22,9 @@ from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) def robotidy_installed() -> bool: @@ -168,10 +170,7 @@ def format_robot_tidy( TextEdit( range=Range( start=Position(line=0, character=0), - end=Position( - line=len(document.get_lines()), - character=0, - ), + end=Position(line=len(document.get_lines()), character=0), ), new_text=new.text, ) @@ -185,7 +184,10 @@ def format_robot_tidy( return None def format_internal( - self, document: TextDocument, options: FormattingOptions, **further_options: Any + self, + document: TextDocument, + options: FormattingOptions, + **further_options: Any, ) -> Optional[List[TextEdit]]: from robot.parsing.model.blocks import File from robot.tidypkg import ( # pyright: ignore [reportMissingImports] @@ -213,10 +215,7 @@ def format_internal( TextEdit( range=Range( start=Position(line=0, character=0), - end=Position( - line=len(document.get_lines()), - character=0, - ), + end=Position(line=len(document.get_lines()), character=0), ), new_text=s.getvalue(), ) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/goto.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/goto.py index 8677744ed..fa8ba5fc7 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/goto.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/goto.py @@ -1,13 +1,6 @@ from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Any, - List, - Optional, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, List, Optional, Union, cast from robotcode.core.concurrent import check_current_thread_canceled from robotcode.core.lsp.types import Location, LocationLink, Position, Range @@ -20,7 +13,9 @@ from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) class RobotGotoProtocolPart(RobotLanguageServerProtocolPart): @@ -62,7 +57,13 @@ def collect( found_range = ( variable.name_range if variable.source == namespace.source and position.is_in_range(variable.name_range, False) - else cast(Optional[Range], next((r.range for r in var_refs if position.is_in_range(r.range)), None)) + else cast( + Optional[Range], + next( + (r.range for r in var_refs if position.is_in_range(r.range)), + None, + ), + ) ) if found_range is not None and variable.source: @@ -91,7 +92,11 @@ def collect( kw.name_range if kw.source == namespace.source and position.is_in_range(kw.name_range, False) else cast( - Optional[Range], next((r.range for r in kw_refs if position.is_in_range(r.range, False)), None) + Optional[Range], + next( + (r.range for r in kw_refs if position.is_in_range(r.range, False)), + None, + ), ) ) @@ -116,7 +121,10 @@ def collect( for ns, ns_refs in all_namespace_refs.items(): for found_range in [ - next((r.range for r in ns_refs if position.is_in_range(r.range, False)), None), + next( + (r.range for r in ns_refs if position.is_in_range(r.range, False)), + None, + ), ns.alias_range if position.is_in_range(ns.alias_range, False) else None, ns.import_range if position.is_in_range(ns.import_range, False) else None, ]: 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 258245abc..8fa8038ec 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 @@ -16,9 +16,19 @@ from robot.parsing.model.blocks import TestCase, TestCaseSection from robot.parsing.model.statements import Documentation, Tags from robotcode.core.concurrent import check_current_thread_canceled -from robotcode.core.lsp.types import Hover, MarkupContent, MarkupKind, Position, Range +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 +from robotcode.robot.utils.ast import ( + get_nodes_at_position, + range_from_node, + range_from_token, +) from robotcode.robot.utils.markdownformatter import MarkDownFormatter from ...common.decorators import language_id @@ -94,7 +104,13 @@ def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: found_range = ( variable.name_range if variable.source == namespace.source and position.is_in_range(variable.name_range, False) - else cast(Optional[Range], next((r.range for r in var_refs if position.is_in_range(r.range)), None)) + else cast( + Optional[Range], + next( + (r.range for r in var_refs if position.is_in_range(r.range)), + None, + ), + ) ) if found_range is not None: @@ -108,7 +124,11 @@ def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: namespace.get_resolvable_variables(nodes, position), ) ) - except (asyncio.CancelledError, SystemExit, KeyboardInterrupt): + except ( + asyncio.CancelledError, + SystemExit, + KeyboardInterrupt, + ): raise except BaseException: self._logger.exception("Error resolving variable: {e}") @@ -124,10 +144,7 @@ def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: if text: text = "| | | |\n|:--|:--|:--|\n" + text return Hover( - contents=MarkupContent( - kind=MarkupKind.MARKDOWN, - value=text, - ), + contents=MarkupContent(kind=MarkupKind.MARKDOWN, value=text), range=highlight_range, ) @@ -142,7 +159,11 @@ def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: kw.name_range if kw.source == namespace.source and position.is_in_range(kw.name_range, False) else cast( - Optional[Range], next((r.range for r in kw_refs if position.is_in_range(r.range, False)), None) + Optional[Range], + next( + (r.range for r in kw_refs if position.is_in_range(r.range, False)), + None, + ), ) ) @@ -168,20 +189,31 @@ def _hover_default(self, nodes: List[ast.AST], document: TextDocument, position: else ns.alias_range if ns.import_source == namespace.source and position.is_in_range(ns.alias_range, False) else cast( - Optional[Range], next((r.range for r in ns_refs if position.is_in_range(r.range, False)), None) + Optional[Range], + next( + (r.range for r in ns_refs if position.is_in_range(r.range, False)), + None, + ), ) ) if found_range is not None: return Hover( - contents=MarkupContent(kind=MarkupKind.MARKDOWN, value=ns.library_doc.to_markdown()), + contents=MarkupContent( + kind=MarkupKind.MARKDOWN, + value=ns.library_doc.to_markdown(), + ), range=found_range, ) return None def hover_TestCase( # noqa: N802 - self, node: ast.AST, nodes: List[ast.AST], document: TextDocument, position: Position + self, + node: ast.AST, + nodes: List[ast.AST], + document: TextDocument, + position: Position, ) -> Optional[Hover]: test_case = cast(TestCase, node) @@ -210,9 +242,6 @@ def hover_TestCase( # noqa: N802 txt += f"{', '.join(tags.values)}\n" return Hover( - contents=MarkupContent( - kind=MarkupKind.MARKDOWN, - value=MarkDownFormatter().format(txt), - ), + contents=MarkupContent(kind=MarkupKind.MARKDOWN, value=MarkDownFormatter().format(txt)), range=range_from_token(name_token), ) 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 3cec7947a..c571cd997 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 @@ -5,8 +5,16 @@ from robotcode.core.concurrent import check_current_thread_canceled 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 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 @@ -20,7 +28,8 @@ from .protocol_part import RobotLanguageServerProtocolPart _HandlerMethod = Callable[ - [TextDocument, Range, ast.AST, ast.AST, Namespace, InlayHintsConfig], Optional[List[InlayHint]] + [TextDocument, Range, ast.AST, ast.AST, Namespace, InlayHintsConfig], + Optional[List[InlayHint]], ] @@ -156,9 +165,7 @@ def _get_inlay_hint( continue arg = kw_arguments[index] - if i >= len(kw_arguments) and arg.kind not in [ - KeywordArgumentKind.VAR_POSITIONAL, - ]: + if i >= len(kw_arguments) and arg.kind != KeywordArgumentKind.VAR_POSITIONAL: break prefix = "" @@ -168,11 +175,18 @@ def _get_inlay_hint( prefix = "**" result.append( - InlayHint(range_from_token(arguments[i]).start, f"{prefix}{arg.name}=", InlayHintKind.PARAMETER) + InlayHint( + range_from_token(arguments[i]).start, + f"{prefix}{arg.name}=", + InlayHintKind.PARAMETER, + ) ) if keyword_token is not None and config.namespaces: - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(namespace, keyword_token) if lib_entry is None and kw_namespace is None: if kw_doc.libtype == "LIBRARY": lib = next( @@ -193,7 +207,12 @@ def _get_inlay_hint( None, ) if lib is not None: - result.append(InlayHint(range_from_token(keyword_token).start, f"{lib.alias or lib.name}.")) + result.append( + InlayHint( + range_from_token(keyword_token).start, + f"{lib.alias or lib.name}.", + ) + ) return result @@ -340,10 +359,7 @@ def handle_VariablesImport( # noqa: N802 try: namespace = self.parent.documents_cache.get_namespace(document) - lib_doc = namespace.get_imported_variables_libdoc( - library_node.name, - library_node.args, - ) + lib_doc = namespace.get_imported_variables_libdoc(library_node.name, library_node.args) if lib_doc is None or lib_doc.errors: lib_doc = namespace.imports_manager.get_libdoc_for_variables_import( diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/inline_value.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/inline_value.py index 28e4716d0..a3243fc8c 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/inline_value.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/inline_value.py @@ -4,9 +4,19 @@ from robot.parsing.lexer.tokens import Token from robot.parsing.model.statements import Statement -from robotcode.core.lsp.types import InlineValue, InlineValueContext, InlineValueEvaluatableExpression, Range +from robotcode.core.lsp.types import ( + InlineValue, + InlineValueContext, + InlineValueEvaluatableExpression, + Range, +) from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.utils.ast import get_nodes_at_position, iter_nodes, range_from_node, range_from_token +from robotcode.robot.utils.ast import ( + get_nodes_at_position, + iter_nodes, + range_from_node, + range_from_token, +) from ...common.decorators import language_id from ...common.text_document import TextDocument @@ -14,7 +24,9 @@ from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) class RobotInlineValueProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin): @@ -65,20 +77,12 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]: ): if token.type == RobotToken.ARGUMENT and isinstance(node, self.get_expression_statement_types()): for t, var in self.iter_expression_variables_from_token( - token, - namespace, - nodes, - context.stopped_location.start, + token, namespace, nodes, context.stopped_location.start ): if var.name != "${CURDIR}": result.append(InlineValueEvaluatableExpression(range_from_token(t), var.name)) - for t, var in self.iter_variables_from_token( - token, - namespace, - nodes, - context.stopped_location.start, - ): + for t, var in self.iter_variables_from_token(token, namespace, nodes, context.stopped_location.start): if var.name != "${CURDIR}": result.append(InlineValueEvaluatableExpression(range_from_token(t), var.name)) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/protocol_part.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/protocol_part.py index 19c4acd82..752be05b5 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/protocol_part.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/protocol_part.py @@ -5,9 +5,7 @@ from robotcode.jsonrpc2.protocol import GenericJsonRPCProtocolPart if TYPE_CHECKING: - from ..protocol import ( - RobotLanguageServerProtocol, - ) + from ..protocol import RobotLanguageServerProtocol class RobotLanguageServerProtocolPart(GenericJsonRPCProtocolPart["RobotLanguageServerProtocol"]): 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 8f2b62275..93ae1abf3 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 @@ -1,19 +1,18 @@ import ast from concurrent.futures import CancelledError -from typing import ( - TYPE_CHECKING, - Any, - Callable, - List, - Optional, - Type, - cast, -) +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Type, cast from robot.parsing.model.statements import Statement from robotcode.core.concurrent import check_current_thread_canceled, threaded from robotcode.core.event import event -from robotcode.core.lsp.types import FileEvent, Location, Position, Range, ReferenceContext, WatchKind +from robotcode.core.lsp.types import ( + FileEvent, + Location, + Position, + Range, + ReferenceContext, + WatchKind, +) from robotcode.core.uri import Uri from robotcode.core.utils.caching import SimpleLRUCache from robotcode.core.utils.logging import LoggingDescriptor @@ -30,7 +29,12 @@ 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.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 @@ -41,7 +45,10 @@ if TYPE_CHECKING: from ..protocol import RobotLanguageServerProtocol -_ReferencesMethod = Callable[[ast.AST, TextDocument, Position, ReferenceContext], Optional[List[Location]]] +_ReferencesMethod = Callable[ + [ast.AST, TextDocument, Position, ReferenceContext], + Optional[List[Location]], +] class RobotReferencesProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin): @@ -100,7 +107,11 @@ def _find_method(self, cls: Type[Any]) -> Optional[_ReferencesMethod]: @language_id("robotframework") @_logger.call def collect( - self, sender: Any, document: TextDocument, position: Position, context: ReferenceContext + self, + sender: Any, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: result_nodes = get_nodes_at_position(self.parent.documents_cache.get_model(document), position) @@ -141,7 +152,11 @@ def _find_references_in_workspace( return result def _references_default( - self, nodes: List[ast.AST], document: TextDocument, position: Position, context: ReferenceContext + self, + nodes: List[ast.AST], + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: namespace = self.parent.documents_cache.get_namespace(document) @@ -166,7 +181,10 @@ def _references_default( return None def has_cached_variable_references( - self, document: TextDocument, variable: VariableDefinition, include_declaration: bool = True + self, + document: TextDocument, + variable: VariableDefinition, + include_declaration: bool = True, ) -> bool: return self._variable_reference_cache.has(document, variable, include_declaration) @@ -178,7 +196,11 @@ def find_variable_references( stop_at_first: bool = False, ) -> List[Location]: return self._variable_reference_cache.get( - self._find_variable_references, document, variable, include_declaration, stop_at_first + self._find_variable_references, + document, + variable, + include_declaration, + stop_at_first, ) def _find_variable_references( @@ -198,14 +220,21 @@ def _find_variable_references( else: result.extend( self._find_references_in_workspace( - document, stop_at_first, self.find_variable_references_in_file, variable, False + document, + stop_at_first, + self.find_variable_references_in_file, + variable, + False, ) ) return result @_logger.call def find_variable_references_in_file( - self, doc: TextDocument, variable: VariableDefinition, include_declaration: bool = True + self, + doc: TextDocument, + variable: VariableDefinition, + include_declaration: bool = True, ) -> List[Location]: try: namespace = self.parent.documents_cache.get_namespace(doc) @@ -274,19 +303,34 @@ def find_keyword_references_in_file( return [] def has_cached_keyword_references( - self, document: TextDocument, kw_doc: KeywordDoc, include_declaration: bool = True + self, + document: TextDocument, + kw_doc: KeywordDoc, + include_declaration: bool = True, ) -> bool: return self._keyword_reference_cache.has(document, kw_doc, include_declaration, False) def find_keyword_references( - self, document: TextDocument, kw_doc: KeywordDoc, include_declaration: bool = True, stop_at_first: bool = False + self, + document: TextDocument, + kw_doc: KeywordDoc, + include_declaration: bool = True, + stop_at_first: bool = False, ) -> List[Location]: return self._keyword_reference_cache.get( - self._find_keyword_references, document, kw_doc, include_declaration, stop_at_first + self._find_keyword_references, + document, + kw_doc, + include_declaration, + stop_at_first, ) def _find_keyword_references( - self, document: TextDocument, kw_doc: KeywordDoc, include_declaration: bool = True, stop_at_first: bool = False + self, + document: TextDocument, + kw_doc: KeywordDoc, + include_declaration: bool = True, + stop_at_first: bool = False, ) -> List[Location]: namespace = self.parent.documents_cache.get_namespace(document) @@ -317,18 +361,19 @@ def _find_keyword_references( result.extend( self._find_references_in_workspace( - document, stop_at_first, self.find_keyword_references_in_file, kw_doc, lib_doc, False + document, + stop_at_first, + self.find_keyword_references_in_file, + kw_doc, + lib_doc, + False, ) ) return result @_logger.call - def _find_library_import_references_in_file( - self, - doc: TextDocument, - library_doc: LibraryDoc, - ) -> List[Location]: + def _find_library_import_references_in_file(self, doc: TextDocument, library_doc: LibraryDoc) -> List[Location]: namespace = self.parent.documents_cache.get_namespace(doc) result: List[Location] = [] @@ -347,11 +392,7 @@ def _find_library_import_references_in_file( return result @_logger.call - def _find_library_alias_in_file( - self, - doc: TextDocument, - entry: LibraryEntry, - ) -> List[Location]: + def _find_library_alias_in_file(self, doc: TextDocument, entry: LibraryEntry) -> List[Location]: namespace = self.parent.documents_cache.get_namespace(doc) references = namespace.get_namespace_references() @@ -361,7 +402,11 @@ def _find_library_alias_in_file( return list(references[entry]) def references_LibraryImport( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: from robot.parsing.lexer.tokens import Token as RobotToken from robot.parsing.model.statements import LibraryImport @@ -381,7 +426,10 @@ def references_LibraryImport( # noqa: N802 return None return self._find_references_in_workspace( - document, False, self._find_library_import_references_in_file, library_doc + document, + False, + self._find_library_import_references_in_file, + library_doc, ) separator = import_node.get_token(RobotToken.WITH_NAME) @@ -410,11 +458,7 @@ def references_LibraryImport( # noqa: N802 return result @_logger.call - def _find_resource_import_references_in_file( - self, - doc: TextDocument, - entry: ResourceEntry, - ) -> List[Location]: + def _find_resource_import_references_in_file(self, doc: TextDocument, entry: ResourceEntry) -> List[Location]: namespace = self.parent.documents_cache.get_namespace(doc) result: List[Location] = [] @@ -432,7 +476,11 @@ def _find_resource_import_references_in_file( return result def references_ResourceImport( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: from robot.parsing.lexer.tokens import Token as RobotToken from robot.parsing.model.statements import ResourceImport @@ -470,17 +518,16 @@ def references_ResourceImport( # noqa: N802 result.append( Location( str(Uri.from_path(entry.library_doc.source)), - Range(start=entry.library_doc.range.start, end=entry.library_doc.range.start), + Range( + start=entry.library_doc.range.start, + end=entry.library_doc.range.start, + ), ) ) return result - def _find_variables_import_references_in_file( - self, - doc: TextDocument, - library_doc: LibraryDoc, - ) -> List[Location]: + def _find_variables_import_references_in_file(self, doc: TextDocument, library_doc: LibraryDoc) -> List[Location]: namespace = self.parent.documents_cache.get_namespace(doc) result: List[Location] = [] @@ -491,7 +538,11 @@ def _find_variables_import_references_in_file( return result def references_VariablesImport( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: from robot.parsing.lexer.tokens import Token as RobotToken from robot.parsing.model.statements import VariablesImport @@ -512,7 +563,10 @@ def references_VariablesImport( # noqa: N802 return None return self._find_references_in_workspace( - document, False, self._find_variables_import_references_in_file, library_doc + document, + False, + self._find_variables_import_references_in_file, + library_doc, ) return None @@ -531,7 +585,12 @@ def find_tag_references_in_file(self, doc: TextDocument, tag: str, is_normalized statements.KeywordTags, ) if get_robot_version() < (7, 0) - else (statements.Tags, statements.TestTags, statements.DefaultTags, statements.KeywordTags) + else ( + statements.Tags, + statements.TestTags, + statements.DefaultTags, + statements.KeywordTags, + ) ) model = self.parent.documents_cache.get_model(doc) @@ -549,7 +608,11 @@ def find_tag_references_in_file(self, doc: TextDocument, tag: str, is_normalized return result def _references_tags( - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: from robot.parsing.lexer.tokens import Token as RobotToken @@ -559,32 +622,52 @@ def _references_tags( token = get_tokens_at_position(cast(Statement, node), position)[-1] - if token.type in [RobotToken.ARGUMENT] and token.value: + if token.type == RobotToken.ARGUMENT and token.value: return self.find_tag_references(document, token.value) return None def find_tag_references(self, document: TextDocument, tag: str) -> List[Location]: return self._find_references_in_workspace( - document, False, self.find_tag_references_in_file, normalize(tag), True + document, + False, + self.find_tag_references_in_file, + normalize(tag), + True, ) def references_ForceTags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: return self._references_tags(node, document, position, context) def references_DefaultTags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: return self._references_tags(node, document, position, context) def references_Tags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: return self._references_tags(node, document, position, context) def references_TestTags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: ReferenceContext + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: ReferenceContext, ) -> Optional[List[Location]]: return self._references_tags(node, document, position, context) 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 595c67fa1..0324f502e 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 @@ -1,5 +1,16 @@ import ast -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Type, TypeVar, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, +) from robot.parsing.lexer.tokens import Token from robot.parsing.model.statements import Statement @@ -17,9 +28,16 @@ WorkspaceEdit, ) from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.diagnostics.entities import VariableDefinition, VariableDefinitionType +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 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 @@ -28,7 +46,9 @@ from .protocol_part import RobotLanguageServerProtocolPart if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) _RenameMethod = Callable[[ast.AST, TextDocument, Position, str], Optional[WorkspaceEdit]] @@ -70,7 +90,9 @@ def collect( new_name: str, ) -> Optional[WorkspaceEdit]: result_nodes = get_nodes_at_position( - self.parent.documents_cache.get_model(document), position, include_end=True + self.parent.documents_cache.get_model(document), + position, + include_end=True, ) if not result_nodes: @@ -92,14 +114,11 @@ def collect( @language_id("robotframework") @_logger.call - def collect_prepare( - self, - sender: Any, - document: TextDocument, - position: Position, - ) -> Optional[PrepareRenameResult]: + def collect_prepare(self, sender: Any, document: TextDocument, position: Position) -> Optional[PrepareRenameResult]: result_nodes = get_nodes_at_position( - self.parent.documents_cache.get_model(document), position, include_end=True + self.parent.documents_cache.get_model(document), + position, + include_end=True, ) if not result_nodes: @@ -287,7 +306,11 @@ def prepare_rename_KeywordCall( # noqa: N802 return self._prepare_rename_keyword(self._find_KeywordCall(node, document, position)) def rename_KeywordCall( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_keyword(document, new_name, self._find_KeywordCall(node, document, position)) @@ -321,7 +344,10 @@ def _find_KeywordCall( # noqa: N802 ): keyword_token = self.strip_bdd_prefix(namespace, keyword_token) - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(namespace, keyword_token) kw_range = range_from_token(keyword_token) @@ -359,7 +385,11 @@ def prepare_rename_KeywordName( # noqa: N802 return self._prepare_rename_keyword(self._find_KeywordName(node, document, position)) def rename_KeywordName( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_keyword(document, new_name, self._find_KeywordName(node, document, position)) @@ -396,7 +426,11 @@ def prepare_rename_Fixture( # noqa: N802 return self._prepare_rename_keyword(self._find_Fixture(node, document, position)) def rename_Fixture( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_keyword(document, new_name, self._find_Fixture(node, document, position)) @@ -435,7 +469,10 @@ def _find_Fixture( # noqa: N802 ): keyword_token = self.strip_bdd_prefix(namespace, keyword_token) - lib_entry, kw_namespace = self.get_namespace_info_from_keyword_token(namespace, keyword_token) + ( + lib_entry, + kw_namespace, + ) = self.get_namespace_info_from_keyword_token(namespace, keyword_token) kw_range = range_from_token(keyword_token) @@ -527,7 +564,11 @@ def prepare_rename_TestTemplate( # noqa: N802 return self._prepare_rename_keyword(self._find_Template_or_TestTemplate(node, document, position)) def rename_TestTemplate( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_keyword( document, @@ -541,7 +582,11 @@ def prepare_rename_Template( # noqa: N802 return self._prepare_rename_keyword(self._find_Template_or_TestTemplate(node, document, position)) def rename_Template( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_keyword( document, @@ -560,7 +605,7 @@ def _prepare_rename_tags( token = tokens[-1] - if token.type in [RobotToken.ARGUMENT] and token.value: + if token.type == RobotToken.ARGUMENT and token.value: return PrepareRenameResultType1(range_from_token(token), token.value) return None @@ -581,7 +626,11 @@ def prepare_rename_Tags( # noqa: N802 return self._prepare_rename_tags(node, document, position) def _rename_tags( - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: from robot.parsing.lexer.tokens import Token as RobotToken @@ -591,7 +640,7 @@ def _rename_tags( token = tokens[-1] - if token.type in [RobotToken.ARGUMENT] and token.value: + if token.type == RobotToken.ARGUMENT and token.value: references = self.parent.robot_references.find_tag_references(document, token.value) changes: List[Union[TextDocumentEdit, CreateFile, RenameFile, DeleteFile]] = [] @@ -612,16 +661,28 @@ def _rename_tags( return None def rename_ForceTags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_tags(node, document, position, new_name) def rename_DefaultTags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_tags(node, document, position, new_name) def rename_Tags( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, new_name: str + self, + node: ast.AST, + document: TextDocument, + position: Position, + new_name: str, ) -> Optional[WorkspaceEdit]: return self._rename_tags(node, document, position, new_name) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py index 1d7d2dfe9..46cf29fb7 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py @@ -141,12 +141,15 @@ def run_check(self, ast_model, filename, source=None): # type: ignore self.parent.documents_cache.get_model(document, False), str(document.uri.to_path()), document.text(), - ) + ) # type: ignore[no-untyped-call] for issue in issues: d = Diagnostic( range=Range( - start=Position(line=max(0, issue.line - 1), character=max(0, issue.col - 1)), + start=Position( + line=max(0, issue.line - 1), + character=max(0, issue.col - 1), + ), end=Position( line=max(0, issue.end_line - 1), character=max(0, issue.end_col - 1), 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 06ff18a6d..a1df076bf 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 @@ -4,11 +4,7 @@ from typing import TYPE_CHECKING, Any, List, Optional from robotcode.core.concurrent import threaded -from robotcode.core.lsp.types import ( - FileChangeType, - FileEvent, - WatchKind, -) +from robotcode.core.lsp.types import FileChangeType, FileEvent, WatchKind from robotcode.core.uri import InvalidUriError, Uri from robotcode.core.utils.glob_path import iter_files from robotcode.core.utils.logging import LoggingDescriptor @@ -64,7 +60,10 @@ def on_file_changed(self, sender: Any, files: List[FileEvent]) -> None: # doc_uri = Uri(fe.uri) try: path = doc_uri.to_path() - if path.suffix in [ROBOT_FILE_EXTENSION, RESOURCE_FILE_EXTENSION]: + if path.suffix in [ + ROBOT_FILE_EXTENSION, + RESOURCE_FILE_EXTENSION, + ]: document = self.parent.documents.get_or_open_document(path) if not document.opened_in_editor: self.parent.documents_cache.get_namespace(document).ensure_initialized() diff --git a/packages/language_server/src/robotcode/language_server/robotframework/parts/selection_range.py b/packages/language_server/src/robotcode/language_server/robotframework/parts/selection_range.py index 083fc30de..ec1c0ea2b 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/parts/selection_range.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/parts/selection_range.py @@ -3,7 +3,12 @@ from robot.parsing.model.statements import Statement from robotcode.core.lsp.types import Position, SelectionRange from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.robot.utils.ast import get_nodes_at_position, get_tokens_at_position, range_from_node, range_from_token +from robotcode.robot.utils.ast import ( + get_nodes_at_position, + get_tokens_at_position, + range_from_node, + range_from_token, +) from ...common.decorators import language_id from ...common.text_document import TextDocument @@ -47,7 +52,11 @@ def collect(self, sender: Any, document: TextDocument, positions: List[Position] if token is not None: current_range = SelectionRange(range_from_token(token), current_range) for var_token, _ in self.iter_variables_from_token( - token, namespace, nodes, position, return_not_found=True + token, + namespace, + nodes, + position, + return_not_found=True, ): var_token_range = range_from_token(var_token) 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 012a6385d..c13e8a008 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 @@ -57,7 +57,11 @@ LibraryDoc, ) from robotcode.robot.utils import get_robot_version -from robotcode.robot.utils.ast import iter_nodes, iter_over_keyword_names_and_owners, token_in_range +from robotcode.robot.utils.ast import ( + iter_nodes, + iter_over_keyword_names_and_owners, + token_in_range, +) from ...common.decorators import language_id from ...common.text_document import TextDocument, range_to_utf16 @@ -69,7 +73,9 @@ from robot.parsing.model.statements import ExceptHeader, WhileHeader if TYPE_CHECKING: - from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol + from robotcode.language_server.robotframework.protocol import ( + RobotLanguageServerProtocol, + ) ROBOT_KEYWORD_INNER = "KEYWORD_INNER" @@ -165,32 +171,87 @@ def namespace_invalidated(self, sender: Any, namespace: Namespace) -> None: def generate_mapping(cls) -> Dict[str, Tuple[Enum, Optional[Set[Enum]]]]: definition: Dict[FrozenSet[str], Tuple[Enum, Optional[Set[Enum]]]] = { frozenset(Token.HEADER_TOKENS): (RobotSemTokenTypes.HEADER, None), - frozenset({Token.SETTING_HEADER}): (RobotSemTokenTypes.HEADER_SETTINGS, None), - frozenset({Token.VARIABLE_HEADER}): (RobotSemTokenTypes.HEADER_VARIABLE, None), - frozenset({Token.TESTCASE_HEADER}): (RobotSemTokenTypes.HEADER_TESTCASE, None), - frozenset({Token.KEYWORD_HEADER}): (RobotSemTokenTypes.HEADER_KEYWORD, None), - frozenset({Token.COMMENT_HEADER}): (RobotSemTokenTypes.HEADER_COMMENT, None), + frozenset({Token.SETTING_HEADER}): ( + RobotSemTokenTypes.HEADER_SETTINGS, + None, + ), + frozenset({Token.VARIABLE_HEADER}): ( + RobotSemTokenTypes.HEADER_VARIABLE, + None, + ), + frozenset({Token.TESTCASE_HEADER}): ( + RobotSemTokenTypes.HEADER_TESTCASE, + None, + ), + frozenset({Token.KEYWORD_HEADER}): ( + RobotSemTokenTypes.HEADER_KEYWORD, + None, + ), + frozenset({Token.COMMENT_HEADER}): ( + RobotSemTokenTypes.HEADER_COMMENT, + None, + ), frozenset({Token.COMMENT}): (SemanticTokenTypes.COMMENT, None), frozenset(Token.SETTING_TOKENS): (RobotSemTokenTypes.SETTING, None), - frozenset({Token.TESTCASE_NAME}): (RobotSemTokenTypes.TESTCASE_NAME, {SemanticTokenModifiers.DECLARATION}), - frozenset({Token.KEYWORD_NAME}): (RobotSemTokenTypes.KEYWORD_NAME, {SemanticTokenModifiers.DECLARATION}), - frozenset({Token.RETURN, Token.FOR, Token.FOR_SEPARATOR, Token.END, Token.IF, Token.ELSE_IF, Token.ELSE}): ( + frozenset({Token.TESTCASE_NAME}): ( + RobotSemTokenTypes.TESTCASE_NAME, + {SemanticTokenModifiers.DECLARATION}, + ), + frozenset({Token.KEYWORD_NAME}): ( + RobotSemTokenTypes.KEYWORD_NAME, + {SemanticTokenModifiers.DECLARATION}, + ), + frozenset( + { + Token.RETURN, + Token.FOR, + Token.FOR_SEPARATOR, + Token.END, + Token.IF, + Token.ELSE_IF, + Token.ELSE, + } + ): ( RobotSemTokenTypes.CONTROL_FLOW, None, ), - frozenset({Token.FOR_SEPARATOR}): (RobotSemTokenTypes.FOR_SEPARATOR, None), + frozenset({Token.FOR_SEPARATOR}): ( + RobotSemTokenTypes.FOR_SEPARATOR, + None, + ), frozenset({Token.ARGUMENT}): (RobotSemTokenTypes.ARGUMENT, None), - frozenset({Token.VARIABLE, Token.ASSIGN}): (RobotSemTokenTypes.VARIABLE, None), + frozenset({Token.VARIABLE, Token.ASSIGN}): ( + RobotSemTokenTypes.VARIABLE, + None, + ), frozenset({Token.KEYWORD}): (RobotSemTokenTypes.KEYWORD, None), - frozenset({ROBOT_KEYWORD_INNER}): (RobotSemTokenTypes.KEYWORD_INNER, None), - frozenset({ROBOT_NAMED_ARGUMENT}): (RobotSemTokenTypes.NAMED_ARGUMENT, None), + frozenset({ROBOT_KEYWORD_INNER}): ( + RobotSemTokenTypes.KEYWORD_INNER, + None, + ), + frozenset({ROBOT_NAMED_ARGUMENT}): ( + RobotSemTokenTypes.NAMED_ARGUMENT, + None, + ), frozenset({ROBOT_OPERATOR}): (SemanticTokenTypes.OPERATOR, None), frozenset({Token.NAME}): (RobotSemTokenTypes.NAME, None), - frozenset({Token.CONTINUATION}): (RobotSemTokenTypes.CONTINUATION, None), + frozenset({Token.CONTINUATION}): ( + RobotSemTokenTypes.CONTINUATION, + None, + ), frozenset({Token.SEPARATOR}): (RobotSemTokenTypes.SEPARATOR, None), - frozenset({Token.EOL, Token.EOS}): (RobotSemTokenTypes.TERMINATOR, None), - frozenset({Token.ERROR, Token.FATAL_ERROR}): (RobotSemTokenTypes.ERROR, None), - frozenset({Token.LIBRARY, Token.RESOURCE, Token.VARIABLES}): (RobotSemTokenTypes.SETTING_IMPORT, None), + frozenset({Token.EOL, Token.EOS}): ( + RobotSemTokenTypes.TERMINATOR, + None, + ), + frozenset({Token.ERROR, Token.FATAL_ERROR}): ( + RobotSemTokenTypes.ERROR, + None, + ), + frozenset({Token.LIBRARY, Token.RESOURCE, Token.VARIABLES}): ( + RobotSemTokenTypes.SETTING_IMPORT, + None, + ), } if get_robot_version() >= (5, 0): @@ -209,26 +270,42 @@ def generate_mapping(cls) -> Dict[str, Tuple[Enum, Optional[Set[Enum]]]]: Token.BREAK, Token.OPTION, } - ): (RobotSemTokenTypes.CONTROL_FLOW, None), + ): (RobotSemTokenTypes.CONTROL_FLOW, None) } ) if get_robot_version() >= (6, 0): definition.update( { - frozenset({Token.CONFIG}): (RobotSemTokenTypes.CONFIG, None), - frozenset({Token.TASK_HEADER}): (RobotSemTokenTypes.HEADER_TASK, None), + frozenset({Token.CONFIG}): ( + RobotSemTokenTypes.CONFIG, + None, + ), + frozenset({Token.TASK_HEADER}): ( + RobotSemTokenTypes.HEADER_TASK, + None, + ), } ) if get_robot_version() >= (7, 0): definition.update( { frozenset({Token.VAR}): (RobotSemTokenTypes.VAR, None), - frozenset({Token.AS}): (RobotSemTokenTypes.SETTING_IMPORT, None), - }, + frozenset({Token.AS}): ( + RobotSemTokenTypes.SETTING_IMPORT, + None, + ), + } ) else: - definition.update({frozenset({Token.WITH_NAME}): (RobotSemTokenTypes.SETTING_IMPORT, None)}) + definition.update( + { + frozenset({Token.WITH_NAME}): ( + RobotSemTokenTypes.SETTING_IMPORT, + None, + ) + } + ) result: Dict[str, Tuple[Enum, Optional[Set[Enum]]]] = {} for k, v in definition.items(): @@ -279,7 +356,13 @@ def generate_sem_sub_tokens( last_index = token.value.rfind("}") if last_index >= 0: - yield SemTokenInfo(token.lineno, col_offset, 2, RobotSemTokenTypes.VARIABLE_BEGIN, sem_mod) + yield SemTokenInfo( + token.lineno, + col_offset, + 2, + RobotSemTokenTypes.VARIABLE_BEGIN, + sem_mod, + ) yield SemTokenInfo.from_token( token, @@ -290,12 +373,20 @@ def generate_sem_sub_tokens( ) yield SemTokenInfo( - token.lineno, col_offset + last_index, 1, RobotSemTokenTypes.VARIABLE_END, sem_mod + token.lineno, + col_offset + last_index, + 1, + RobotSemTokenTypes.VARIABLE_END, + sem_mod, ) if length - last_index - 1 > 0: yield SemTokenInfo.from_token( - token, sem_type, sem_mod, col_offset + last_index + 1, length - last_index - 1 + token, + sem_type, + sem_mod, + col_offset + last_index + 1, + length - last_index - 1, ) else: yield SemTokenInfo.from_token(token, sem_type, sem_mod) @@ -318,7 +409,14 @@ def generate_sem_sub_tokens( elif token.type in [Token.KEYWORD, ROBOT_KEYWORD_INNER] or ( token.type == Token.NAME and isinstance(node, (Fixture, Template, TestTemplate)) ): - if namespace.find_keyword(token.value, raise_keyword_error=False, handle_bdd_style=False) is None: + if ( + namespace.find_keyword( + token.value, + raise_keyword_error=False, + handle_bdd_style=False, + ) + is None + ): bdd_len = 0 if get_robot_version() < (6, 0): @@ -340,9 +438,19 @@ def generate_sem_sub_tokens( if bdd_len > 0: yield SemTokenInfo.from_token( - token, RobotSemTokenTypes.BDD_PREFIX, sem_mod, token.col_offset, bdd_len + token, + RobotSemTokenTypes.BDD_PREFIX, + sem_mod, + token.col_offset, + bdd_len, + ) + yield SemTokenInfo.from_token( + token, + sem_type, + sem_mod, + token.col_offset + bdd_len, + 1, ) - yield SemTokenInfo.from_token(token, sem_type, sem_mod, token.col_offset + bdd_len, 1) token = Token( token.type, @@ -359,7 +467,10 @@ def generate_sem_sub_tokens( kw: str = token.value kw_doc = namespace.find_keyword(token.value, raise_keyword_error=False) - lib_entry, kw_namespace = cls.get_namespace_info_from_keyword_token(namespace, token) + ( + lib_entry, + kw_namespace, + ) = cls.get_namespace_info_from_keyword_token(namespace, token) if lib_entry is not None and kw_doc: if kw_doc.parent != lib_entry.library_doc: kw_namespace = None @@ -374,7 +485,7 @@ def generate_sem_sub_tokens( col_offset, len(kw_namespace), RobotSemTokenTypes.NAMESPACE, - {RobotSemTokenModifiers.BUILTIN} if cls.BUILTIN_MATCHER == kw_namespace else None, + {RobotSemTokenModifiers.BUILTIN} if kw_namespace == cls.BUILTIN_MATCHER else None, ) yield SemTokenInfo( token.lineno, @@ -400,7 +511,11 @@ def generate_sem_sub_tokens( for i in range(1, m.lastindex + 1): arg_start, arg_end = m.span(i) yield SemTokenInfo.from_token( - token, sem_type, sem_mod, col_offset + kw_index + start, arg_start - start + token, + sem_type, + sem_mod, + col_offset + kw_index + start, + arg_start - start, ) yield SemTokenInfo.from_token( token, @@ -413,7 +528,11 @@ def generate_sem_sub_tokens( if start < end: yield SemTokenInfo.from_token( - token, sem_type, sem_mod, col_offset + kw_index + start, end - start + token, + sem_type, + sem_mod, + col_offset + kw_index + start, + end - start, ) else: @@ -432,7 +551,13 @@ def generate_sem_sub_tokens( g.end() - g.start(), ) else: - yield SemTokenInfo.from_token(token, RobotSemTokenTypes.NAMESPACE, sem_mod, col_offset, length) + yield SemTokenInfo.from_token( + token, + RobotSemTokenTypes.NAMESPACE, + sem_mod, + col_offset, + length, + ) elif get_robot_version() >= (5, 0) and token.type == Token.OPTION: if ( isinstance(node, ExceptHeader) @@ -444,11 +569,27 @@ def generate_sem_sub_tokens( col_offset = token.col_offset name, value = token.value.split("=", 1) - yield SemTokenInfo.from_token(token, RobotSemTokenTypes.VARIABLE, sem_mod, col_offset, len(name)) yield SemTokenInfo.from_token( - token, SemanticTokenTypes.OPERATOR, sem_mod, col_offset + len(name), 1 + token, + RobotSemTokenTypes.VARIABLE, + sem_mod, + col_offset, + len(name), + ) + yield SemTokenInfo.from_token( + token, + SemanticTokenTypes.OPERATOR, + sem_mod, + col_offset + len(name), + 1, + ) + yield SemTokenInfo.from_token( + token, + sem_type, + sem_mod, + col_offset + len(name) + 1, + len(value), ) - yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset + len(name) + 1, len(value)) else: yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset, length) elif ( @@ -461,7 +602,13 @@ def generate_sem_sub_tokens( yield SemTokenInfo.from_token(token, SemanticTokenTypes.OPERATOR, sem_mod, col_offset, 1) yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset + 1, length - 2) - yield SemTokenInfo.from_token(token, SemanticTokenTypes.OPERATOR, sem_mod, col_offset + length - 1, 1) + yield SemTokenInfo.from_token( + token, + SemanticTokenTypes.OPERATOR, + sem_mod, + col_offset + length - 1, + 1, + ) else: yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset, length) @@ -496,10 +643,21 @@ def generate_sem_tokens( else SemanticTokenTypes.PARAMETER, ) yield SemTokenInfo.from_token( - Token(ROBOT_OPERATOR, "=", token.lineno, token.col_offset + length), + Token( + ROBOT_OPERATOR, + "=", + token.lineno, + token.col_offset + length, + ), SemanticTokenTypes.OPERATOR, ) - token = Token(token.type, value, token.lineno, token.col_offset + length + 1, token.error) + token = Token( + token.type, + value, + token.lineno, + token.col_offset + length + 1, + token.error, + ) elif isinstance(node, Arguments) and name: yield SemTokenInfo.from_token( Token( @@ -510,7 +668,13 @@ def generate_sem_tokens( ), RobotSemTokenTypes.NAMED_ARGUMENT, ) - token = Token(token.type, "", token.lineno, token.col_offset + len(name), token.error) + token = Token( + token.type, + "", + token.lineno, + token.col_offset + len(name), + token.error, + ) for sub_token in self._tokenize_variables( token, @@ -536,7 +700,7 @@ def generate_run_kw_tokens( def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: nonlocal arguments while arguments and arguments[0] and arguments[0].type in Token.NON_DATA_TOKENS: - yield arguments[0], node, + yield (arguments[0], node) arguments = arguments[1:] if kw_doc is not None and kw_doc.is_any_run_keyword(): @@ -551,7 +715,13 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: namespace, builtin_library_doc, namespace.find_keyword(unescape(token.value), raise_keyword_error=False), - Token(ROBOT_KEYWORD_INNER, token.value, token.lineno, token.col_offset, token.error), + Token( + ROBOT_KEYWORD_INNER, + token.value, + token.lineno, + token.col_offset, + token.error, + ), arguments[1:], node, ): @@ -559,7 +729,7 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: elif kw_doc.is_run_keyword_with_condition() and len(arguments) > 0: cond_count = kw_doc.run_keyword_condition_count() for _ in range(cond_count): - yield arguments[0], node, + yield (arguments[0], node) arguments = arguments[1:] for b in skip_non_data_tokens(): @@ -571,7 +741,13 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: namespace, builtin_library_doc, namespace.find_keyword(unescape(token.value), raise_keyword_error=False), - Token(ROBOT_KEYWORD_INNER, token.value, token.lineno, token.col_offset, token.error), + Token( + ROBOT_KEYWORD_INNER, + token.value, + token.lineno, + token.col_offset, + token.error, + ), arguments[1:], node, ): @@ -589,7 +765,16 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: arguments = arguments[1:] if token.value == "AND": - yield Token(Token.ELSE, token.value, token.lineno, token.col_offset, token.error), node + yield ( + Token( + Token.ELSE, + token.value, + token.lineno, + token.col_offset, + token.error, + ), + node, + ) continue separator_token = next((e for e in arguments if e.value == "AND"), None) @@ -607,7 +792,13 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: namespace, builtin_library_doc, namespace.find_keyword(unescape(token.value), raise_keyword_error=False), - Token(ROBOT_KEYWORD_INNER, token.value, token.lineno, token.col_offset, token.error), + Token( + ROBOT_KEYWORD_INNER, + token.value, + token.lineno, + token.col_offset, + token.error, + ), args, node, ): @@ -617,7 +808,7 @@ def skip_non_data_tokens() -> Iterator[Tuple[Token, ast.AST]]: def generate_run_kw_if() -> Iterator[Tuple[Token, ast.AST]]: nonlocal arguments - yield arguments[0], node, + yield (arguments[0], node) arguments = arguments[1:] while arguments: @@ -631,7 +822,16 @@ def generate_run_kw_if() -> Iterator[Tuple[Token, ast.AST]]: arguments = arguments[1:] if token.value in ["ELSE", "ELSE IF"]: - yield Token(Token.ELSE, token.value, token.lineno, token.col_offset, token.error), node + yield ( + Token( + Token.ELSE, + token.value, + token.lineno, + token.col_offset, + token.error, + ), + node, + ) if token.value == "ELSE IF": for b in skip_non_data_tokens(): @@ -647,9 +847,16 @@ def generate_run_kw_if() -> Iterator[Tuple[Token, ast.AST]]: inner_kw_doc = namespace.find_keyword(unescape(token.value), raise_keyword_error=False) if inner_kw_doc is not None and inner_kw_doc.is_run_keyword_if(): - yield Token( - ROBOT_KEYWORD_INNER, token.value, token.lineno, token.col_offset, token.error - ), node + yield ( + Token( + ROBOT_KEYWORD_INNER, + token.value, + token.lineno, + token.col_offset, + token.error, + ), + node, + ) arguments = arguments[1:] @@ -661,7 +868,10 @@ def generate_run_kw_if() -> Iterator[Tuple[Token, ast.AST]]: continue - separator_token = next((e for e in arguments if e.value in ["ELSE", "ELSE IF"]), None) + separator_token = next( + (e for e in arguments if e.value in ["ELSE", "ELSE IF"]), + None, + ) args: Sequence[Token] = [] if separator_token is not None: @@ -675,7 +885,13 @@ def generate_run_kw_if() -> Iterator[Tuple[Token, ast.AST]]: namespace, builtin_library_doc, inner_kw_doc, - Token(ROBOT_KEYWORD_INNER, token.value, token.lineno, token.col_offset, token.error), + Token( + ROBOT_KEYWORD_INNER, + token.value, + token.lineno, + token.col_offset, + token.error, + ), args, node, ): @@ -698,7 +914,7 @@ def generate_keyword_tokens( yield kw_token, node for token in arguments: - if token.type in [Token.ARGUMENT]: + if token.type == Token.ARGUMENT: name, value = split_from_equals(token.value) if value is not None: if kw_doc is None: @@ -708,10 +924,35 @@ def generate_keyword_tokens( v for v in kw_doc.arguments if v.kind == KeywordArgumentKind.VAR_NAMED or v.name == name ): length = len(name) - yield Token(ROBOT_NAMED_ARGUMENT, name, token.lineno, token.col_offset), node + yield ( + Token( + ROBOT_NAMED_ARGUMENT, + name, + token.lineno, + token.col_offset, + ), + node, + ) - yield Token(ROBOT_OPERATOR, "=", token.lineno, token.col_offset + length), node - yield Token(token.type, value, token.lineno, token.col_offset + length + 1, token.error), node + yield ( + Token( + ROBOT_OPERATOR, + "=", + token.lineno, + token.col_offset + length, + ), + node, + ) + yield ( + Token( + token.type, + value, + token.lineno, + token.col_offset + length + 1, + token.error, + ), + node, + ) continue @@ -753,16 +994,35 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]: ) ): length = len(name) - yield Token(ROBOT_NAMED_ARGUMENT, name, token.lineno, token.col_offset), node + yield ( + Token( + ROBOT_NAMED_ARGUMENT, + name, + token.lineno, + token.col_offset, + ), + node, + ) - yield Token(ROBOT_OPERATOR, "=", token.lineno, token.col_offset + length), node - yield Token( - token.type, - value, - token.lineno, - token.col_offset + length + 1, - token.error, - ), node + yield ( + Token( + ROBOT_OPERATOR, + "=", + token.lineno, + token.col_offset + length, + ), + node, + ) + yield ( + Token( + token.type, + value, + token.lineno, + token.col_offset + length + 1, + token.error, + ), + node, + ) continue @@ -786,16 +1046,35 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]: ) ): length = len(name) - yield Token(ROBOT_NAMED_ARGUMENT, name, token.lineno, token.col_offset), node + yield ( + Token( + ROBOT_NAMED_ARGUMENT, + name, + token.lineno, + token.col_offset, + ), + node, + ) - yield Token(ROBOT_OPERATOR, "=", token.lineno, token.col_offset + length), node - yield Token( - token.type, - value, - token.lineno, - token.col_offset + length + 1, - token.error, - ), node + yield ( + Token( + ROBOT_OPERATOR, + "=", + token.lineno, + token.col_offset + length, + ), + node, + ) + yield ( + Token( + token.type, + value, + token.lineno, + token.col_offset + length + 1, + token.error, + ), + node, + ) continue @@ -868,7 +1147,10 @@ def get_tokens() -> Iterator[Tuple[Token, ast.AST]]: lines, Range( start=Position(line=token.lineno - 1, character=token.col_offset), - end=Position(line=token.lineno - 1, character=token.col_offset + token.length), + end=Position( + line=token.lineno - 1, + character=token.col_offset + token.length, + ), ), ) token_col_offset = token_range.start.character @@ -940,6 +1222,10 @@ def collect_range( @language_id("robotframework") @_logger.call def collect_full_delta( - self, sender: Any, document: TextDocument, previous_result_id: str, **kwargs: Any - ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None]: + self, + sender: Any, + document: TextDocument, + previous_result_id: str, + **kwargs: Any, + ) -> Union[SemanticTokens, SemanticTokensDelta, SemanticTokensDeltaPartialResult, None,]: return None 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 8a5230283..7bbb2edd5 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 @@ -1,6 +1,15 @@ import ast from concurrent.futures import CancelledError -from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, Tuple, Type, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Optional, + Sequence, + Tuple, + Type, + cast, +) from robot.parsing.lexer.tokens import Token from robot.parsing.model.statements import Statement @@ -15,9 +24,17 @@ ) 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 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.decorators import ( + language_id, + retrigger_characters, + trigger_characters, +) from ...common.text_document import TextDocument from ..diagnostics.model_helper import ModelHelperMixin from .protocol_part import RobotLanguageServerProtocolPart @@ -26,7 +43,8 @@ from ..protocol import RobotLanguageServerProtocol _SignatureHelpMethod = Callable[ - [ast.AST, TextDocument, Position, Optional[SignatureHelpContext]], Optional[SignatureHelp] + [ast.AST, TextDocument, Position, Optional[SignatureHelpContext]], + Optional[SignatureHelp], ] @@ -57,10 +75,16 @@ def _find_method(self, cls: Type[Any]) -> Optional[_SignatureHelpMethod]: @retrigger_characters([" ", "\t"]) @_logger.call def collect( - self, sender: Any, document: TextDocument, position: Position, context: Optional[SignatureHelpContext] = None + self, + sender: Any, + document: TextDocument, + position: Position, + context: Optional[SignatureHelpContext] = None, ) -> Optional[SignatureHelp]: result_node = get_node_at_position( - self.parent.documents_cache.get_model(document, False), position, include_end=True + self.parent.documents_cache.get_model(document, False), + position, + include_end=True, ) if result_node is None: return None @@ -90,7 +114,11 @@ def _signature_help_KeywordCall_or_Fixture( # noqa: N802 token_at_position = tokens_at_position[-1] - if token_at_position.type not in [RobotToken.ARGUMENT, RobotToken.EOL, RobotToken.SEPARATOR]: + if token_at_position.type not in [ + RobotToken.ARGUMENT, + RobotToken.EOL, + RobotToken.SEPARATOR, + ]: return None keyword_doc_and_token: Optional[Tuple[Optional[KeywordDoc], Token]] = None @@ -164,14 +192,22 @@ def _get_signature_help( ) def signature_help_KeywordCall( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: Optional[SignatureHelpContext] = None + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: Optional[SignatureHelpContext] = None, ) -> Optional[SignatureHelp]: from robot.parsing.lexer.tokens import Token as RobotToken return self._signature_help_KeywordCall_or_Fixture(RobotToken.KEYWORD, node, document, position, context) def signature_help_Fixture( # noqa: N802 - self, node: ast.AST, document: TextDocument, position: Position, context: Optional[SignatureHelpContext] = None + self, + node: ast.AST, + document: TextDocument, + position: Position, + context: Optional[SignatureHelpContext] = None, ) -> Optional[SignatureHelp]: from robot.parsing.lexer.tokens import Token as RobotToken from robot.parsing.model.statements import Fixture @@ -229,7 +265,11 @@ def signature_help_LibraryImport( # noqa: N802 token_at_position = tokens_at_position[-1] - if token_at_position.type not in [RobotToken.ARGUMENT, RobotToken.EOL, RobotToken.SEPARATOR]: + if token_at_position.type not in [ + RobotToken.ARGUMENT, + RobotToken.EOL, + RobotToken.SEPARATOR, + ]: return None if not lib_doc.inits: @@ -289,7 +329,11 @@ def signature_help_VariablesImport( # noqa: N802 token_at_position = tokens_at_position[-1] - if token_at_position.type not in [RobotToken.ARGUMENT, RobotToken.EOL, RobotToken.SEPARATOR]: + if token_at_position.type not in [ + RobotToken.ARGUMENT, + RobotToken.EOL, + RobotToken.SEPARATOR, + ]: return None if not lib_doc.inits: diff --git a/packages/language_server/src/robotcode/language_server/robotframework/protocol.py b/packages/language_server/src/robotcode/language_server/robotframework/protocol.py index 699cad4bd..2a7352a43 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/protocol.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/protocol.py @@ -2,20 +2,40 @@ import sys from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Any, ClassVar, Dict, Final, List, Optional, Set +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + Final, + List, + Optional, + Set, +) from robotcode.core.lsp.types import InitializeError from robotcode.core.utils.dataclasses import CamelSnakeMixin, from_dict from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.jsonrpc2.protocol import JsonRPCErrorException, JsonRPCErrors, ProtocolPartDescriptor -from robotcode.language_server.common.parts.document_symbols import symbol_information_label -from robotcode.language_server.common.protocol import LanguageDefinition, LanguageServerProtocol +from robotcode.jsonrpc2.protocol import ( + JsonRPCErrorException, + JsonRPCErrors, + ProtocolPartDescriptor, +) +from robotcode.language_server.common.parts.document_symbols import ( + symbol_information_label, +) +from robotcode.language_server.common.protocol import ( + LanguageDefinition, + LanguageServerProtocol, +) from robotcode.robot.config.model import RobotBaseProfile from robotcode.robot.utils import get_robot_version from ..__version__ import __version__ from .configuration import RobotConfig -from .parts.code_action_documentation import RobotCodeActionDocumentationProtocolPart +from .parts.code_action_documentation import ( + RobotCodeActionDocumentationProtocolPart, +) from .parts.code_action_quick_fixes import RobotCodeActionQuickFixesProtocolPart from .parts.code_action_refactor import RobotCodeActionRefactorProtocolPart from .parts.code_lens import RobotCodeLensProtocolPart @@ -108,7 +128,13 @@ class RobotLanguageServerProtocol(LanguageServerProtocol): short_name = "RobotCode" version = __version__ - file_extensions: ClassVar[Set[str]] = {"robot", "resource", "py", "yaml", "yml"} + file_extensions: ClassVar[Set[str]] = { + "robot", + "resource", + "py", + "yaml", + "yml", + } languages: ClassVar[List[LanguageDefinition]] = [ LanguageDefinition( @@ -124,7 +150,11 @@ class RobotLanguageServerProtocol(LanguageServerProtocol): LanguageDefinition(id="markdown", extensions=[".md"]), ] - def __init__(self, server: "RobotLanguageServer", profile: Optional[RobotBaseProfile] = None): + def __init__( + self, + server: "RobotLanguageServer", + profile: Optional[RobotBaseProfile] = None, + ): super().__init__(server) self.profile = profile if profile is not None else RobotBaseProfile() self.options = Options() @@ -155,7 +185,9 @@ def _on_initialize(self, sender: Any, initialization_options: Optional[Any] = No check_robotframework() except RobotCodeError as e: raise JsonRPCErrorException( - JsonRPCErrors.INTERNAL_ERROR, f"Can't start language server: {e}", InitializeError(retry=False) + JsonRPCErrors.INTERNAL_ERROR, + f"Can't start language server: {e}", + InitializeError(retry=False), ) from e self.workspace.did_change_configuration.add(self._on_did_change_configuration) diff --git a/packages/language_server/src/robotcode/language_server/robotframework/server.py b/packages/language_server/src/robotcode/language_server/robotframework/server.py index 9ad58d275..f77fc0a97 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/server.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/server.py @@ -1,7 +1,10 @@ from typing import Optional from robotcode.core.types import ServerMode, TcpParams -from robotcode.language_server.common.server import TCP_DEFAULT_PORT, LanguageServerBase +from robotcode.language_server.common.server import ( + TCP_DEFAULT_PORT, + LanguageServerBase, +) from robotcode.robot.config.model import RobotBaseProfile from .protocol import RobotLanguageServerProtocol diff --git a/packages/plugin/src/robotcode/plugin/__init__.py b/packages/plugin/src/robotcode/plugin/__init__.py index dbdedf738..25f1858bd 100644 --- a/packages/plugin/src/robotcode/plugin/__init__.py +++ b/packages/plugin/src/robotcode/plugin/__init__.py @@ -3,7 +3,18 @@ from dataclasses import dataclass from enum import Enum, unique from pathlib import Path -from typing import IO, Any, AnyStr, Callable, Iterable, Optional, Sequence, TypeVar, Union, cast +from typing import ( + IO, + Any, + AnyStr, + Callable, + Iterable, + Optional, + Sequence, + TypeVar, + Union, + cast, +) import click import pluggy @@ -79,7 +90,10 @@ def show_diagnostics(self, value: bool) -> None: @property def colored(self) -> bool: - return self.config.colored_output in [ColoredOutput.AUTO, ColoredOutput.YES] + return self.config.colored_output in [ + ColoredOutput.AUTO, + ColoredOutput.YES, + ] def verbose( self, @@ -115,7 +129,10 @@ def warning( ) def print_data( - self, data: Any, remove_defaults: bool = True, default_output_format: Optional[OutputFormat] = None + self, + data: Any, + remove_defaults: bool = True, + default_output_format: Optional[OutputFormat] = None, ) -> None: format = self.config.output_format or default_output_format or OutputFormat.TEXT @@ -131,7 +148,11 @@ def print_data( if text is None: if format in [OutputFormat.JSON, OutputFormat.JSON_INDENT]: - text = as_json(data, indent=format == OutputFormat.JSON_INDENT, compact=format == OutputFormat.TEXT) + text = as_json( + data, + indent=format == OutputFormat.JSON_INDENT, + compact=format == OutputFormat.TEXT, + ) else: text = str(data) @@ -238,7 +259,10 @@ def echo_via_pager( ) click.echo(text, color=color if color is not None else self.colored) else: - click.echo_via_pager(text_or_generator, color=color if color is not None else self.colored) + click.echo_via_pager( + text_or_generator, + color=color if color is not None else self.colored, + ) def keyboard_interrupt(self) -> None: self.verbose("Aborted!", file=sys.stderr) diff --git a/packages/plugin/src/robotcode/plugin/click_helper/options.py b/packages/plugin/src/robotcode/plugin/click_helper/options.py index c4e8b5f2e..f0733ac0d 100644 --- a/packages/plugin/src/robotcode/plugin/click_helper/options.py +++ b/packages/plugin/src/robotcode/plugin/click_helper/options.py @@ -19,7 +19,9 @@ def server_options( - default_server_mode: ServerMode, default_port: int, allowed_server_modes: Optional[Set[ServerMode]] = None + default_server_mode: ServerMode, + default_port: int, + allowed_server_modes: Optional[Set[ServerMode]] = None, ) -> Sequence[FC]: result: List[FC] = [] diff --git a/packages/plugin/src/robotcode/plugin/click_helper/types.py b/packages/plugin/src/robotcode/plugin/click_helper/types.py index 2a98cdb40..28fde088f 100644 --- a/packages/plugin/src/robotcode/plugin/click_helper/types.py +++ b/packages/plugin/src/robotcode/plugin/click_helper/types.py @@ -23,7 +23,12 @@ class EnumChoice(click.Choice, Generic[T]): """A click.Choice that accepts Enum values.""" - def __init__(self, choices: Type[T], case_sensitive: bool = True, excluded: Optional[Set[T]] = None) -> None: + def __init__( + self, + choices: Type[T], + case_sensitive: bool = True, + excluded: Optional[Set[T]] = None, + ) -> None: super().__init__( choices if excluded is None else (set(choices).difference(excluded)), # type: ignore case_sensitive, @@ -74,7 +79,12 @@ class AddressesPort(NamedTuple): class AddressPortParamType(click.ParamType): name = "[
:]" - def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> Any: + def convert( + self, + value: Any, + param: Optional[click.Parameter], + ctx: Optional[click.Context], + ) -> Any: splitted = value.split(":") if len(splitted) == 1 and splitted[0]: try: diff --git a/packages/plugin/src/robotcode/plugin/manager.py b/packages/plugin/src/robotcode/plugin/manager.py index 4b1df4016..1c34828d0 100644 --- a/packages/plugin/src/robotcode/plugin/manager.py +++ b/packages/plugin/src/robotcode/plugin/manager.py @@ -20,7 +20,9 @@ def cli_commands(self) -> List[List[click.Command]]: ) @property - def config_classes(self) -> List[List[Tuple[str, Type[specs.TConfigClass]]]]: + def config_classes( + self, + ) -> List[List[Tuple[str, Type[specs.TConfigClass]]]]: return cast( List[List[Tuple[str, Type[specs.TConfigClass]]]], self._plugin_manager.hook.register_config_classes(), diff --git a/packages/robot/src/robotcode/robot/config/loader.py b/packages/robot/src/robotcode/robot/config/loader.py index 84e3fb021..9611e23e8 100644 --- a/packages/robot/src/robotcode/robot/config/loader.py +++ b/packages/robot/src/robotcode/robot/config/loader.py @@ -79,7 +79,10 @@ def load_config_from_pyproject_toml_str(config_type: Type[_ConfigType], tool_nam def _load_config_data_from_path( - config_type: Type[_ConfigType], tool_name: str, robot_toml_tool_name: Optional[str], __path: Path + config_type: Type[_ConfigType], + tool_name: str, + robot_toml_tool_name: Optional[str], + __path: Path, ) -> _ConfigType: try: if __path.name == PYPROJECT_TOML: @@ -87,7 +90,9 @@ def _load_config_data_from_path( if __path.name == ROBOT_TOML or __path.name == LOCAL_ROBOT_TOML or __path.suffix == ".toml": return load_config_from_robot_toml_str( - config_type, __path.read_text("utf-8"), tool_name=robot_toml_tool_name + config_type, + __path.read_text("utf-8"), + tool_name=robot_toml_tool_name, ) raise TypeError("Unknown config file type.") @@ -115,18 +120,25 @@ def load_config_from_path( for __path in __paths: result.add_options( _load_config_data_from_path( - config_type, tool_name, robot_toml_tool_name, __path if isinstance(__path, Path) else __path[0] + config_type, + tool_name, + robot_toml_tool_name, + __path if isinstance(__path, Path) else __path[0], ) ) return result -def load_robot_config_from_path(*__paths: Union[Path, Tuple[Path, ConfigType]]) -> RobotConfig: +def load_robot_config_from_path( + *__paths: Union[Path, Tuple[Path, ConfigType]], +) -> RobotConfig: return load_config_from_path(RobotConfig, *__paths, tool_name="robot") -def find_project_root(*sources: Union[str, Path]) -> Tuple[Optional[Path], DiscoverdBy]: +def find_project_root( + *sources: Union[str, Path], +) -> Tuple[Optional[Path], DiscoverdBy]: if not sources: sources = (str(Path.cwd().absolute()),) @@ -158,7 +170,9 @@ def find_project_root(*sources: Union[str, Path]) -> Tuple[Optional[Path], Disco return None, DiscoverdBy.NOT_FOUND -def get_config_files_from_folder(folder: Path) -> Sequence[Tuple[Path, ConfigType]]: +def get_config_files_from_folder( + folder: Path, +) -> Sequence[Tuple[Path, ConfigType]]: result = [] pyproject_toml = folder / PYPROJECT_TOML diff --git a/packages/robot/src/robotcode/robot/config/model.py b/packages/robot/src/robotcode/robot/config/model.py index dd618298b..bac100871 100644 --- a/packages/robot/src/robotcode/robot/config/model.py +++ b/packages/robot/src/robotcode/robot/config/model.py @@ -22,7 +22,12 @@ ) import tomli_w -from robotcode.core.utils.dataclasses import TypeValidationError, ValidateMixin, as_dict, validate_types +from robotcode.core.utils.dataclasses import ( + TypeValidationError, + ValidateMixin, + as_dict, + validate_types, +) from robotcode.core.utils.safe_eval import safe_eval from typing_extensions import Self @@ -260,7 +265,10 @@ def append_name(field: "dataclasses.Field[Any]", add_flag: Optional[str] = None) if value is None or value == Flag.DEFAULT: continue - append_name(field, bool(value) != field.metadata.get("robot_flag_default", True)) + append_name( + field, + bool(value) != field.metadata.get("robot_flag_default", True), + ) continue @@ -292,7 +300,11 @@ def append_name(field: "dataclasses.Field[Any]", add_flag: Optional[str] = None) def _verified_value(name: str, value: Any, types: Union[type, Tuple[type, ...]], target: Any) -> Any: errors = validate_types(types, value) if errors: - raise TypeValidationError("Dataclass Type Validation Error", target=target, errors={name: errors}) + raise TypeValidationError( + "Dataclass Type Validation Error", + target=target, + errors={name: errors}, + ) return value def add_options(self, config: "BaseOptions", combine_extras: bool = False) -> None: @@ -305,7 +317,10 @@ def add_options(self, config: "BaseOptions", combine_extras: bool = False) -> No continue new = self._verified_value( - f.name, getattr(config, f.name), type_hints[f.name[EXTEND_PREFIX_LEN:]], config + f.name, + getattr(config, f.name), + type_hints[f.name[EXTEND_PREFIX_LEN:]], + config, ) if new is None: continue @@ -447,7 +462,16 @@ class CommonOptions(BaseOptions): robot_name="expandkeywords", robot_priority=500, ) - flatten_keywords: Optional[List[Union[str, Literal["for", "while", "iteration"], NamePattern, TagPattern]]] = field( + flatten_keywords: Optional[ + List[ + Union[ + str, + Literal["for", "while", "iteration"], + NamePattern, + TagPattern, + ] + ] + ] = field( description="""\ Flattens matching keywords in the generated log file. Matching keywords get all log messages from their @@ -613,7 +637,14 @@ class CommonOptions(BaseOptions): robot_short_name="P", ) remove_keywords: Optional[ - List[Union[str, Literal["all", "passed", "for", "wuks"], NamePattern, TagPattern]] + List[ + Union[ + str, + Literal["all", "passed", "for", "wuks"], + NamePattern, + TagPattern, + ] + ] ] = field( description="""\ Remove keyword data from the generated log file. @@ -909,7 +940,7 @@ class CommonExtraOptions(BaseOptions): matched using same rules as with --include. corresponds to the `-e --exclude tag *` option of _robot_ - """, + """ ) extend_expand_keywords: Optional[List[Union[str, NamePattern, TagPattern]]] = field( description="""\ @@ -927,10 +958,17 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `--expandkeywords name:|tag: *` option of _robot_ - """, + """ ) extend_flatten_keywords: Optional[ - List[Union[str, Literal["for", "while", "iteration"], NamePattern, TagPattern]] + List[ + Union[ + str, + Literal["for", "while", "iteration"], + NamePattern, + TagPattern, + ] + ] ] = field( description="""\ Appends entries to the --flattenkeywords option. @@ -950,7 +988,7 @@ class CommonExtraOptions(BaseOptions): `--removekeywords tag:` corresponds to the `--flattenkeywords for|while|iteration|name:|tag: *` option of _robot_ - """, + """ ) extend_includes: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -970,7 +1008,7 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `-i --include tag *` option of _robot_ - """, + """ ) extend_metadata: Optional[Dict[str, Union[str, StringExpression]]] = field( description="""\ @@ -981,7 +1019,7 @@ class CommonExtraOptions(BaseOptions): as --doc. Example: --metadata Version:1.2 corresponds to the `-M --metadata name:value *` option of _robot_ - """, + """ ) extend_parse_include: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -995,7 +1033,7 @@ class CommonExtraOptions(BaseOptions): all files in that directory, recursively. corresponds to the `-I --parseinclude pattern *` option of _robot_ - """, + """ ) extend_pre_rebot_modifiers: Optional[Dict[str, List[Union[str, StringExpression]]]] = field( description="""\ @@ -1006,7 +1044,7 @@ class CommonExtraOptions(BaseOptions): arguments the same way as with --listener. corresponds to the `--prerebotmodifier modifier *` option of _robot_ - """, + """ ) extend_python_path: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1027,10 +1065,17 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `-P --pythonpath path *` option of _robot_ - """, + """ ) extend_remove_keywords: Optional[ - List[Union[str, Literal["all", "passed", "for", "wuks"], NamePattern, TagPattern]] + List[ + Union[ + str, + Literal["all", "passed", "for", "wuks"], + NamePattern, + TagPattern, + ] + ] ] = field( description="""\ Appends entries to the --removekeywords option. @@ -1075,7 +1120,7 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `--removekeywords all|passed|for|wuks|name:|tag: *` option of _robot_ - """, + """ ) extend_set_tag: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1084,7 +1129,7 @@ class CommonExtraOptions(BaseOptions): Sets given tag(s) to all executed tests. corresponds to the `-G --settag tag *` option of _robot_ - """, + """ ) extend_suites: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1099,7 +1144,7 @@ class CommonExtraOptions(BaseOptions): selects suite `Y` only if its parent is `X`. corresponds to the `-s --suite name *` option of _robot_ - """, + """ ) extend_tag_doc: Optional[Dict[str, Union[str, StringExpression]]] = field( description="""\ @@ -1119,7 +1164,7 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `--tagdoc pattern:doc *` option of _robot_ - """, + """ ) extend_tag_stat_combine: Optional[List[Union[str, Dict[str, str]]]] = field( description="""\ @@ -1139,7 +1184,7 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `--tagstatcombine tags:name *` option of _robot_ - """, + """ ) extend_tag_stat_exclude: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1150,7 +1195,7 @@ class CommonExtraOptions(BaseOptions): similarly as --exclude is used with --include. corresponds to the `--tagstatexclude tag *` option of _robot_ - """, + """ ) extend_tag_stat_include: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1161,7 +1206,7 @@ class CommonExtraOptions(BaseOptions): Given tag can be a pattern like with --include. corresponds to the `--tagstatinclude tag *` option of _robot_ - """, + """ ) extend_tag_stat_link: Optional[Dict[str, Union[str, StringExpression]]] = field( description="""\ @@ -1181,7 +1226,7 @@ class CommonExtraOptions(BaseOptions): ``` corresponds to the `--tagstatlink pattern:link:title *` option of _robot_ - """, + """ ) extend_tasks: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1190,7 +1235,7 @@ class CommonExtraOptions(BaseOptions): Alias to --test. Especially applicable with --rpa. corresponds to the `--task name *` option of _robot_ - """, + """ ) extend_tests: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1204,7 +1249,7 @@ class CommonExtraOptions(BaseOptions): in brackets. corresponds to the `-t --test name *` option of _robot_ - """, + """ ) @@ -1589,7 +1634,7 @@ class RobotExtraOptions(BaseOptions): a custom language file. corresponds to the `--language lang *` option of _rebot_ - """, + """ ) extend_listeners: Optional[Dict[str, List[Union[str, StringExpression]]]] = field( description="""\ @@ -1608,7 +1653,7 @@ class RobotExtraOptions(BaseOptions): ``` corresponds to the `--listener listener *` option of _rebot_ - """, + """ ) extend_parsers: Optional[Dict[str, List[Union[str, StringExpression]]]] = field( description="""\ @@ -1618,7 +1663,7 @@ class RobotExtraOptions(BaseOptions): arguments the same way as with --listener. corresponds to the `--parser parser *` option of _rebot_ - """, + """ ) extend_pre_run_modifiers: Optional[Dict[str, List[Union[str, StringExpression]]]] = field( description="""\ @@ -1629,7 +1674,7 @@ class RobotExtraOptions(BaseOptions): same way as with --listener. corresponds to the `--prerunmodifier modifier *` option of _rebot_ - """, + """ ) extend_skip: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1639,7 +1684,7 @@ class RobotExtraOptions(BaseOptions): a pattern. corresponds to the `--skip tag *` option of _rebot_ - """, + """ ) extend_skip_on_failure: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1649,7 +1694,7 @@ class RobotExtraOptions(BaseOptions): Tag can be a pattern corresponds to the `--skiponfailure tag *` option of _rebot_ - """, + """ ) extend_variables: Optional[Dict[str, Union[str, StringExpression]]] = field( description="""\ @@ -1669,7 +1714,7 @@ class RobotExtraOptions(BaseOptions): ``` corresponds to the `-v --variable name:value *` option of _rebot_ - """, + """ ) extend_variable_files: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -1687,7 +1732,7 @@ class RobotExtraOptions(BaseOptions): ``` corresponds to the `-V --variablefile path *` option of _rebot_ - """, + """ ) @@ -1889,7 +1934,7 @@ class LibDocExtraOptions(BaseOptions): and resources. corresponds to the `-P --pythonpath path *` option of _libdoc_ - """, + """ ) @@ -2002,7 +2047,7 @@ class TestDocExtraOptions(BaseOptions): Exclude tests by tags. corresponds to the `-e --exclude tag *` option of _testdoc_ - """, + """ ) extend_includes: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -2011,7 +2056,7 @@ class TestDocExtraOptions(BaseOptions): Include tests by tags. corresponds to the `-i --include tag *` option of _testdoc_ - """, + """ ) extend_metadata: Optional[Dict[str, Union[str, StringExpression]]] = field( description="""\ @@ -2020,7 +2065,7 @@ class TestDocExtraOptions(BaseOptions): Set/override metadata of the top level suite. corresponds to the `-M --metadata name:value *` option of _testdoc_ - """, + """ ) extend_set_tag: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -2029,7 +2074,7 @@ class TestDocExtraOptions(BaseOptions): Set given tag(s) to all test cases. corresponds to the `-G --settag tag *` option of _testdoc_ - """, + """ ) extend_suites: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -2038,7 +2083,7 @@ class TestDocExtraOptions(BaseOptions): Include suites by name. corresponds to the `-s --suite name *` option of _testdoc_ - """, + """ ) extend_tests: Optional[List[Union[str, StringExpression]]] = field( description="""\ @@ -2047,7 +2092,7 @@ class TestDocExtraOptions(BaseOptions): Include tests by name. corresponds to the `-t --test name *` option of _testdoc_ - """, + """ ) @@ -2107,7 +2152,7 @@ class RobotBaseProfile(CommonOptions, CommonExtraOptions, RobotOptions, RobotExt TEST_VAR = "test" SECRET = "password" ``` - """, + """ ) rebot: Optional[RebotProfile] = field( @@ -2141,13 +2186,13 @@ class RobotExtraBaseProfile(RobotBaseProfile): extend_args: Optional[List[str]] = field( description="""\ Append extra arguments to be passed to _robot_. - """, + """ ) extend_env: Optional[Dict[str, str]] = field( description="""\ Append extra environment variables to be set before tests. - """, + """ ) extend_paths: Union[str, List[str], None] = field( @@ -2172,7 +2217,7 @@ class RobotProfile(RobotExtraBaseProfile): description="""\ The profile should be detached. Detached means it is not inherited from the main profile. - """, + """ ) enabled: Union[bool, Condition, None] = field( @@ -2216,22 +2261,18 @@ class RobotConfig(RobotExtraBaseProfile): ```toml default_profiles = ["default", "Firefox"] ``` - """, - ) - profiles: Optional[Dict[str, RobotProfile]] = field( - description="Execution profiles.", + """ ) + profiles: Optional[Dict[str, RobotProfile]] = field(description="Execution profiles.") - extend_profiles: Optional[Dict[str, RobotProfile]] = field( - description="Extra execution profiles.", - ) + extend_profiles: Optional[Dict[str, RobotProfile]] = field(description="Extra execution profiles.") - tool: Any = field( - description="Tool configurations.", - ) + tool: Any = field(description="Tool configurations.") def select_profiles( - self, *names: str, verbose_callback: Optional[Callable[..., None]] = None + self, + *names: str, + verbose_callback: Optional[Callable[..., None]] = None, ) -> Dict[str, RobotProfile]: result: Dict[str, RobotProfile] = {} @@ -2246,7 +2287,7 @@ def select_profiles( ) if verbose_callback and default_profile: - verbose_callback(f"Using default profiles: {', '.join( default_profile)}.") + verbose_callback(f"Using default profiles: {', '.join(default_profile)}.") names = (*(default_profile or ()),) @@ -2261,7 +2302,11 @@ def select_profiles( return result - def combine_profiles(self, *names: str, verbose_callback: Optional[Callable[..., None]] = None) -> RobotBaseProfile: + def combine_profiles( + self, + *names: str, + verbose_callback: Optional[Callable[..., None]] = None, + ) -> RobotBaseProfile: type_hints = get_type_hints(RobotBaseProfile) base_field_names = [f.name for f in dataclasses.fields(RobotBaseProfile)] @@ -2298,7 +2343,10 @@ def combine_profiles(self, *names: str, verbose_callback: Optional[Callable[..., for f in dataclasses.fields(profile): if f.name.startswith("extend_"): new = self._verified_value( - f.name, getattr(profile, f.name), type_hints[f.name[EXTEND_PREFIX_LEN:]], profile + f.name, + getattr(profile, f.name), + type_hints[f.name[EXTEND_PREFIX_LEN:]], + profile, ) if new is None: continue @@ -2308,7 +2356,11 @@ def combine_profiles(self, *names: str, verbose_callback: Optional[Callable[..., setattr(result, f.name[EXTEND_PREFIX_LEN:], new) else: if isinstance(old, dict): - setattr(result, f.name[EXTEND_PREFIX_LEN:], {**old, **new}) + setattr( + result, + f.name[EXTEND_PREFIX_LEN:], + {**old, **new}, + ) elif isinstance(old, list): setattr(result, f.name[EXTEND_PREFIX_LEN:], [*old, *new]) elif isinstance(old, tuple): @@ -2323,7 +2375,12 @@ def combine_profiles(self, *names: str, verbose_callback: Optional[Callable[..., if getattr(profile, f"extend_{f.name}", None) is not None: continue - new = self._verified_value(f.name, getattr(profile, f.name), type_hints[f.name], profile) + new = self._verified_value( + f.name, + getattr(profile, f.name), + type_hints[f.name], + profile, + ) if new is not None: setattr(result, f.name, new) diff --git a/packages/robot/src/robotcode/robot/config/utils.py b/packages/robot/src/robotcode/robot/config/utils.py index 744bc68a9..14c5d9598 100644 --- a/packages/robot/src/robotcode/robot/config/utils.py +++ b/packages/robot/src/robotcode/robot/config/utils.py @@ -3,11 +3,18 @@ import platformdirs -from .loader import ConfigType, DiscoverdBy, find_project_root, get_config_files_from_folder, get_default_config +from .loader import ( + ConfigType, + DiscoverdBy, + find_project_root, + get_config_files_from_folder, + get_default_config, +) def get_user_config_file( - create: bool = True, verbose_callback: Optional[Callable[[str], None]] = None + create: bool = True, + verbose_callback: Optional[Callable[[str], None]] = None, ) -> Optional[Path]: result = Path(platformdirs.user_config_dir("robotcode", appauthor=False), "robot.toml") if result.is_file(): @@ -58,16 +65,17 @@ def get_config_files( if verbose_callback: if result: - verbose_callback( - "Found configuration files:\n " + "\n ".join(str(f[0]) for f in result), - ) + verbose_callback("Found configuration files:\n " + "\n ".join(str(f[0]) for f in result)) else: verbose_callback("No configuration files found.") user_config = get_user_config_file(verbose_callback=verbose_callback) return ( - [*([(user_config, ConfigType.USER_DEFAULT_CONFIG_TOML)] if user_config else []), *result], + [ + *([(user_config, ConfigType.USER_DEFAULT_CONFIG_TOML)] if user_config else []), + *result, + ], root_folder, discovered_by, ) diff --git a/packages/robot/src/robotcode/robot/diagnostics/entities.py b/packages/robot/src/robotcode/robot/diagnostics/entities.py index 31ecb8030..9e434a366 100644 --- a/packages/robot/src/robotcode/robot/diagnostics/entities.py +++ b/packages/robot/src/robotcode/robot/diagnostics/entities.py @@ -1,6 +1,15 @@ from dataclasses import dataclass, field from enum import Enum -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, TypeVar, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + List, + Optional, + Tuple, + TypeVar, + cast, +) from robotcode.core.lsp.types import Position, Range from robotcode.robot.utils.ast import range_from_token @@ -46,7 +55,15 @@ def range(self) -> Range: @single_call def __hash__(self) -> int: - return hash((self.line_no, self.col_offset, self.end_line_no, self.end_col_offset, self.source)) + return hash( + ( + self.line_no, + self.col_offset, + self.end_line_no, + self.end_col_offset, + self.source, + ) + ) @dataclass @@ -89,26 +106,14 @@ def alias_range(self) -> Range: @single_call def __hash__(self) -> int: - return hash( - ( - type(self), - self.name, - self.args, - self.alias, - ) - ) + return hash((type(self), self.name, self.args, self.alias)) @dataclass class ResourceImport(Import): @single_call def __hash__(self) -> int: - return hash( - ( - type(self), - self.name, - ) - ) + return hash((type(self), self.name)) @dataclass @@ -117,13 +122,7 @@ class VariablesImport(Import): @single_call def __hash__(self) -> int: - return hash( - ( - type(self), - self.name, - self.args, - ) - ) + return hash((type(self), self.name, self.args)) class InvalidVariableError(Exception): @@ -215,14 +214,8 @@ def name_range(self) -> Range: @property def range(self) -> Range: return Range( - start=Position( - line=self.line_no - 1, - character=self.col_offset, - ), - end=Position( - line=self.end_line_no - 1, - character=self.end_col_offset, - ), + start=Position(line=self.line_no - 1, character=self.col_offset), + end=Position(line=self.end_line_no - 1, character=self.end_col_offset), ) @@ -322,9 +315,9 @@ class LibraryEntry: library_doc: "LibraryDoc" = field(compare=False) args: Tuple[Any, ...] = () alias: Optional[str] = None - import_range: Range = field(default_factory=lambda: Range.zero()) + import_range: Range = field(default_factory=Range.zero) import_source: Optional[str] = None - alias_range: Range = field(default_factory=lambda: Range.zero()) + alias_range: Range = field(default_factory=Range.zero) def __str__(self) -> str: result = self.import_name @@ -357,7 +350,15 @@ class ResourceEntry(LibraryEntry): @single_call def __hash__(self) -> int: - return hash((type(self), self.name, self.import_name, self.import_range, self.import_source)) + return hash( + ( + type(self), + self.name, + self.import_name, + self.import_range, + self.import_source, + ) + ) @dataclass @@ -366,4 +367,13 @@ class VariablesEntry(LibraryEntry): @single_call def __hash__(self) -> int: - return hash((type(self), self.name, self.import_name, self.args, self.import_range, self.import_source)) + return hash( + ( + type(self), + self.name, + self.import_name, + self.args, + self.import_range, + self.import_source, + ) + ) diff --git a/packages/robot/src/robotcode/robot/diagnostics/library_doc.py b/packages/robot/src/robotcode/robot/diagnostics/library_doc.py index c9f7cc921..db6210fcb 100644 --- a/packages/robot/src/robotcode/robot/diagnostics/library_doc.py +++ b/packages/robot/src/robotcode/robot/diagnostics/library_doc.py @@ -43,7 +43,11 @@ 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.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 @@ -103,7 +107,12 @@ JSON_EXTENSIONS = {".json", ".rsrc"} ALLOWED_RESOURCE_FILE_EXTENSIONS = ( - {ROBOT_FILE_EXTENSION, RESOURCE_FILE_EXTENSION, *REST_EXTENSIONS, *JSON_EXTENSIONS} + { + ROBOT_FILE_EXTENSION, + RESOURCE_FILE_EXTENSION, + *REST_EXTENSIONS, + *JSON_EXTENSIONS, + } if get_robot_version() >= (6, 1) else {ROBOT_FILE_EXTENSION, RESOURCE_FILE_EXTENSION, *REST_EXTENSIONS} ) @@ -121,7 +130,11 @@ def convert_from_rest(text: str) -> str: try: from docutils.core import publish_parts - parts = publish_parts(text, writer_name="html5", settings_overrides={"syntax_highlight": "none"}) + parts = publish_parts( + text, + writer_name="html5", + settings_overrides={"syntax_highlight": "none"}, + ) return str(parts["html_body"]) @@ -149,7 +162,12 @@ def is_embedded_keyword(name: str) -> bool: class KeywordMatcher: - def __init__(self, name: str, can_have_embedded: bool = True, is_namespace: bool = False) -> None: + def __init__( + self, + name: str, + can_have_embedded: bool = True, + is_namespace: bool = False, + ) -> None: self.name = name self._can_have_embedded = can_have_embedded and not is_namespace self._is_namespace = is_namespace @@ -182,7 +200,7 @@ def embedded_arguments(self) -> Any: return self._embedded_arguments - def __eq__(self, o: Any) -> bool: + def __eq__(self, o: object) -> bool: if isinstance(o, KeywordMatcher): if self._is_namespace != o._is_namespace: return False @@ -208,7 +226,7 @@ def __hash__(self) -> int: return hash( (self.embedded_arguments.name, tuple(self.embedded_arguments.args)) if self.embedded_arguments - else (self.normalized_name, self._is_namespace), + else (self.normalized_name, self._is_namespace) ) def __str__(self) -> str: @@ -277,10 +295,10 @@ def __hash__(self) -> int: def to_markdown(self, header_level: int = 2) -> str: result = "" - result += f"##{'#'*header_level} {self.name} ({self.type})\n\n" + result += f"##{'#' * header_level} {self.name} ({self.type})\n\n" if self.doc: - result += f"###{'#'*header_level} Documentation:\n\n" + result += f"###{'#' * header_level} Documentation:\n\n" result += "\n\n" if self.doc_format == ROBOT_DOC_FORMAT: @@ -291,17 +309,17 @@ def to_markdown(self, header_level: int = 2) -> str: result += self.doc if self.members: - result += f"\n\n###{'#'*header_level} Allowed Values:\n\n" + result += f"\n\n###{'#' * header_level} Allowed Values:\n\n" result += "- " + "\n- ".join(f"`{m.name}`" for m in self.members) if self.items: - result += f"\n\n###{'#'*header_level} Dictionary Structure:\n\n" + result += f"\n\n###{'#' * header_level} Dictionary Structure:\n\n" result += "```\n{" result += "\n ".join(f"'{m.key}': <{m.type}> {'# optional' if not m.required else ''}" for m in self.items) result += "\n}\n```" if self.accepts: - result += f"\n\n###{'#'*header_level} Converted Types:\n\n" + result += f"\n\n###{'#' * header_level} Converted Types:\n\n" result += "- " + "\n- ".join(self.accepts) return result @@ -515,7 +533,10 @@ def resolve( resolve_args_until=resolve_variables_until, dict_to_kwargs=dict_to_kwargs, ) - return cast(Tuple[List[Any], List[Tuple[str, Any]]], resolver.resolve(arguments, variables)) + return cast( + Tuple[List[Any], List[Tuple[str, Any]]], + resolver.resolve(arguments, variables), + ) class MyNamedArgumentResolver(NamedArgumentResolver): def _raise_positional_after_named(self) -> None: @@ -646,19 +667,20 @@ def range(self) -> Range: return Range( start=Position( line=self.line_no - 1 if self.line_no > 0 else 0, - character=self.col_offset if self.col_offset >= 0 else 0, + character=max(self.col_offset, 0), ), end=Position( - line=self.end_line_no - 1 if self.end_line_no >= 0 else self.line_no if self.line_no > 0 else 0, - character=self.end_col_offset - if self.end_col_offset >= 0 - else self.col_offset - if self.col_offset >= 0 - else 0, + line=self.end_line_no - 1 if self.end_line_no >= 0 else max(0, self.line_no), + character=self.end_col_offset if self.end_col_offset >= 0 else max(self.col_offset, 0), ), ) - def to_markdown(self, add_signature: bool = True, header_level: int = 2, add_type: bool = True) -> str: + def to_markdown( + self, + add_signature: bool = True, + header_level: int = 2, + add_type: bool = True, + ) -> str: result = "" if add_signature: @@ -668,7 +690,7 @@ def to_markdown(self, add_signature: bool = True, header_level: int = 2, add_typ if result: result += "\n\n" - result += f"##{'#'*header_level} Documentation:\n" + result += f"##{'#' * header_level} Documentation:\n" if self.doc_format == ROBOT_DOC_FORMAT: result += MarkDownFormatter().format(self.doc) @@ -682,17 +704,17 @@ def to_markdown(self, add_signature: bool = True, header_level: int = 2, add_typ def _get_signature(self, header_level: int, add_type: bool = True) -> str: if add_type: result = ( - f"#{'#'*header_level} " + f"#{'#' * header_level} " f"{(self.libtype or 'Library').capitalize() if self.is_initializer else 'Keyword'} *{self.name}*\n" ) else: if not self.is_initializer: - result = f"\n\n#{'#'*header_level} {self.name}\n" + result = f"\n\n#{'#' * header_level} {self.name}\n" else: result = "" if self.arguments: - result += f"\n##{'#'*header_level} Arguments: \n" + result += f"\n##{'#' * header_level} Arguments: \n" result += "\n| | | | |" result += "\n|:--|:--|:--|:--|" @@ -701,7 +723,10 @@ def _get_signature(self, header_level: int, add_type: bool = True) -> str: for a in self.arguments: prefix = "" - if a.kind in [KeywordArgumentKind.NAMED_ONLY_MARKER, KeywordArgumentKind.POSITIONAL_ONLY_MARKER]: + if a.kind in [ + KeywordArgumentKind.NAMED_ONLY_MARKER, + KeywordArgumentKind.POSITIONAL_ONLY_MARKER, + ]: continue if a.kind == KeywordArgumentKind.VAR_POSITIONAL: @@ -745,7 +770,11 @@ def signature(self) -> str: + ", ".join( str(a) for a in self.arguments - if a.kind not in [KeywordArgumentKind.POSITIONAL_ONLY_MARKER, KeywordArgumentKind.NAMED_ONLY_MARKER] + if a.kind + not in [ + KeywordArgumentKind.POSITIONAL_ONLY_MARKER, + KeywordArgumentKind.NAMED_ONLY_MARKER, + ] ) + ")" ) @@ -756,7 +785,11 @@ def parameter_signature(self, full_signatures: Optional[Sequence[int]] = None) - + ", ".join( a.signature(full_signatures is None or i in full_signatures) for i, a in enumerate(self.arguments) - if a.kind not in [KeywordArgumentKind.POSITIONAL_ONLY_MARKER, KeywordArgumentKind.NAMED_ONLY_MARKER] + if a.kind + not in [ + KeywordArgumentKind.POSITIONAL_ONLY_MARKER, + KeywordArgumentKind.NAMED_ONLY_MARKER, + ] ) + ")" ) @@ -774,12 +807,12 @@ def is_run_keyword(self) -> bool: return self.libname == BUILTIN_LIBRARY_NAME and self.name in RUN_KEYWORD_NAMES def is_run_keyword_with_condition(self) -> bool: - return self.libname == BUILTIN_LIBRARY_NAME and self.name in RUN_KEYWORD_WITH_CONDITION_NAMES.keys() + return self.libname == BUILTIN_LIBRARY_NAME and self.name in RUN_KEYWORD_WITH_CONDITION_NAMES def run_keyword_condition_count(self) -> int: return ( RUN_KEYWORD_WITH_CONDITION_NAMES[self.name] - if self.libname == BUILTIN_LIBRARY_NAME and self.name in RUN_KEYWORD_WITH_CONDITION_NAMES.keys() + if self.libname == BUILTIN_LIBRARY_NAME and self.name in RUN_KEYWORD_WITH_CONDITION_NAMES else 0 ) @@ -813,7 +846,12 @@ def __hash__(self) -> int: class KeywordError(Exception): - def __init__(self, *args: Any, multiple_keywords: Optional[List[KeywordDoc]] = None, **kwargs: Any) -> None: + def __init__( + self, + *args: Any, + multiple_keywords: Optional[List[KeywordDoc]] = None, + **kwargs: Any, + ) -> None: super().__init__(*args, **kwargs) self.multiple_keywords = multiple_keywords @@ -830,7 +868,7 @@ def _matchers(self) -> Dict[KeywordMatcher, KeywordDoc]: self.__matchers = {v.matcher: v for v in self.keywords} return self.__matchers - def __getitem__(self, key: str) -> "KeywordDoc": + def __getitem__(self, key: str) -> KeywordDoc: items = [(k, v) for k, v in self._matchers.items() if k == key] if not items: @@ -852,7 +890,10 @@ def __getitem__(self, key: str) -> "KeywordDoc": file_info = "File" error = [f"{file_info} contains multiple keywords matching name '{key}':"] names = sorted(k.name for k, v in items) - raise KeywordError("\n ".join(error + names), multiple_keywords=[v for _, v in items]) + raise KeywordError( + "\n ".join(error + names), + multiple_keywords=[v for _, v in items], + ) def __contains__(self, __x: object) -> bool: return any(k == __x for k in self._matchers.keys()) @@ -1009,12 +1050,17 @@ def range(self) -> Range: return Range( start=Position(line=self.line_no - 1 if self.line_no >= 0 else 0, character=0), end=Position( - line=self.end_line_no - 1 if self.end_line_no >= 0 else self.line_no if self.line_no >= 0 else 0, + line=self.end_line_no - 1 if self.end_line_no >= 0 else max(self.line_no, 0), character=0, ), ) - def to_markdown(self, add_signature: bool = True, only_doc: bool = True, header_level: int = 2) -> str: + def to_markdown( + self, + add_signature: bool = True, + only_doc: bool = True, + header_level: int = 2, + ) -> str: with io.StringIO(newline="\n") as result: def write_lines(*args: str) -> None: @@ -1025,15 +1071,12 @@ def write_lines(*args: str) -> None: write_lines(i.to_markdown(header_level=header_level), "", "---") write_lines( - f"#{'#'*header_level} {(self.type.capitalize()) if self.type else 'Unknown'} *{self.name}*", + f"#{'#' * header_level} {(self.type.capitalize()) if self.type else 'Unknown'} *{self.name}*", "", ) if self.version or self.scope: - write_lines( - "| | |", - "| :--- | :--- |", - ) + write_lines("| | |", "| :--- | :--- |") if self.version: write_lines(f"| **Library Version:** | {self.version} |") @@ -1043,7 +1086,7 @@ def write_lines(*args: str) -> None: write_lines("", "") if self.doc: - write_lines(f"##{'#'*header_level} Introduction", "") + write_lines(f"##{'#' * header_level} Introduction", "") if self.doc_format == ROBOT_DOC_FORMAT: doc = MarkDownFormatter().format(self.doc) @@ -1093,7 +1136,7 @@ def _get_doc_for_keywords(self, header_level: int = 2) -> str: result = "" if any(v for v in self.inits.values() if v.arguments): result += "\n---\n\n" - result += f"\n##{'#'*header_level} Importing\n\n" + result += f"\n##{'#' * header_level} Importing\n\n" first = True @@ -1106,7 +1149,7 @@ def _get_doc_for_keywords(self, header_level: int = 2) -> str: if self.keywords: result += "\n---\n\n" - result += f"\n##{'#'*header_level} Keywords\n\n" + result += f"\n##{'#' * header_level} Keywords\n\n" first = True @@ -1168,7 +1211,12 @@ class VariablesDoc(LibraryDoc): variables: List[ImportedVariableDefinition] = field(default_factory=list) - def to_markdown(self, add_signature: bool = True, only_doc: bool = True, header_level: int = 2) -> str: + def to_markdown( + self, + add_signature: bool = True, + only_doc: bool = True, + header_level: int = 2, + ) -> str: result = super().to_markdown(add_signature, only_doc, header_level) if self.variables: @@ -1342,7 +1390,9 @@ class MessageAndTraceback(NamedTuple): __RE_TRACEBACK = re.compile(r'^ +File +"(.*)", +line +(\d+).*$', re.MULTILINE) -def get_message_and_traceback_from_exception_text(text: str) -> MessageAndTraceback: +def get_message_and_traceback_from_exception_text( + text: str, +) -> MessageAndTraceback: splitted = __RE_MESSAGE.split(text, 1) return MessageAndTraceback( @@ -1353,7 +1403,11 @@ def get_message_and_traceback_from_exception_text(text: str) -> MessageAndTraceb ) -def error_from_exception(ex: BaseException, default_source: Optional[str], default_line_no: Optional[int]) -> Error: +def error_from_exception( + ex: BaseException, + default_source: Optional[str], + default_line_no: Optional[int], +) -> Error: message_and_traceback = get_message_and_traceback_from_exception_text(str(ex)) if message_and_traceback.traceback: tr = message_and_traceback.traceback[-1] @@ -1379,7 +1433,7 @@ def error_from_exception(ex: BaseException, default_source: Optional[str], defau @dataclass -class _Variable(object): +class _Variable: name: str value: Iterable[str] source: Optional[str] = None @@ -1610,9 +1664,7 @@ def __init__(self) -> None: def write(self, message: str, level: str, html: bool = False) -> None: self.messages.append((message, level, html)) - def import_test_library( - name: str, - ) -> Union[Any, Tuple[Any, str]]: + def import_test_library(name: str) -> Union[Any, Tuple[Any, str]]: with OutputCapturer(library_import=True): importer = Importer("test library", LOGGER) return importer.import_class_or_module(name, return_source=True) @@ -1659,7 +1711,7 @@ def get_test_library( # skip antigravity easter egg # see https://python-history.blogspot.com/2010/06/import-antigravity.html - if import_name.lower() in ["antigravity"] or import_name.lower().endswith("antigravity.py"): + if import_name.lower() == "antigravity" or import_name.lower().endswith("antigravity.py"): raise IgnoreEasterEggLibraryWarning(f"Ignoring import for python easter egg '{import_name}'.") errors: List[Error] = [] @@ -1882,7 +1934,9 @@ def get_args_to_process(libdoc_name: str, kw_name: str) -> Any: ) if get_robot_version() >= (6, 1): - from robot.libdocpkg.datatypes import TypeDoc as RobotTypeDoc + from robot.libdocpkg.datatypes import ( + TypeDoc as RobotTypeDoc, + ) from robot.running.arguments.argumentspec import TypeInfo def _yield_type_info(info: TypeInfo) -> Iterable[TypeInfo]: @@ -1899,7 +1953,10 @@ def _get_type_docs(keywords: List[Any], custom_converters: List[Any]) -> Set[Rob for type_info in _yield_type_info(arg.type): if type_info.type is not None: if get_robot_version() < (7, 0): - type_doc = RobotTypeDoc.for_type(type_info.type, custom_converters) + type_doc = RobotTypeDoc.for_type( + type_info.type, + custom_converters, + ) else: type_doc = RobotTypeDoc.for_type(type_info, custom_converters) if type_doc: @@ -1922,7 +1979,10 @@ def _get_type_docs(keywords: List[Any], custom_converters: List[Any]) -> Set[Rob libtype=libdoc.type, doc_format=libdoc.doc_format, ) - for td in _get_type_docs([kw[0] for kw in keyword_docs + init_keywords], lib.converters) + for td in _get_type_docs( + [kw[0] for kw in keyword_docs + init_keywords], + lib.converters, + ) ] except (SystemExit, KeyboardInterrupt): @@ -2051,7 +2111,7 @@ def get_variables_doc( # skip antigravity easter egg # see https://python-history.blogspot.com/2010/06/import-antigravity.html - if import_name.lower() in ["antigravity"] or import_name.lower().endswith("antigravity.py"): + if import_name.lower() == "antigravity" or import_name.lower().endswith("antigravity.py"): raise IgnoreEasterEggLibraryWarning(f"Ignoring import for python easter egg '{import_name}'.") class MyPythonImporter(PythonImporter): @@ -2068,7 +2128,9 @@ def is_dynamic(self) -> bool: if get_robot_version() >= (5, 0): libcode, source = module_importer.import_class_or_module( - import_name, instantiate_with_args=(), return_source=True + import_name, + instantiate_with_args=(), + return_source=True, ) else: source = import_name @@ -2129,7 +2191,12 @@ def _get_initial_handler(self, library: Any, name: Any, method: Any) -> Any: ) for kw in [ (KeywordDocBuilder().build_keyword(k), k) - for k in [KeywordWrapper(vars_initializer, libdoc.source or "")] + for k in [ + KeywordWrapper( + vars_initializer, + libdoc.source or "", + ) + ] ] ] ) @@ -2151,7 +2218,12 @@ def _get_initial_handler(self, library: Any, name: Any, method: Any) -> Any: return None if get_variables is not None: - vars_initializer = InitVarHandler(libdoc, get_variables.__name__, get_variables, None) + vars_initializer = InitVarHandler( + libdoc, + get_variables.__name__, + get_variables, + None, + ) libdoc.inits = KeywordStore( keywords=[ @@ -2171,8 +2243,16 @@ def _get_initial_handler(self, library: Any, name: Any, method: Any) -> Any: is_initializer=True, ) for kw in [ - (KeywordDocBuilder().build_keyword(k), k) - for k in [KeywordWrapper(vars_initializer, libdoc.source or "")] + ( + KeywordDocBuilder().build_keyword(k), + k, + ) + for k in [ + KeywordWrapper( + vars_initializer, + libdoc.source or "", + ) + ] ] ] ) @@ -2526,7 +2606,9 @@ def get_model_doc( from robot.running.usererrorhandler import UserErrorHandler from robot.running.userkeyword import UserLibrary else: - from robot.running.invalidkeyword import InvalidKeyword as UserErrorHandler + from robot.running.invalidkeyword import ( + InvalidKeyword as UserErrorHandler, + ) from robot.running.resourcemodel import ResourceFile errors: List[Error] = [] @@ -2540,12 +2622,26 @@ def get_model_doc( error = node.error if isinstance(node, HasError) else None if error is not None: - errors.append(Error(message=error, type_name="ModelError", source=source, line_no=node.lineno)) + errors.append( + Error( + message=error, + type_name="ModelError", + source=source, + line_no=node.lineno, + ) + ) if append_model_errors: node_errors = node.errors if isinstance(node, HasErrors) else None if node_errors is not None: for e in node_errors: - errors.append(Error(message=e, type_name="ModelError", source=source, line_no=node.lineno)) + errors.append( + Error( + message=e, + type_name="ModelError", + source=source, + line_no=node.lineno, + ) + ) def get_keyword_name_token_from_line(line: int) -> Optional[Token]: for keyword_name in keyword_name_nodes: @@ -2554,12 +2650,17 @@ def get_keyword_name_token_from_line(line: int) -> Optional[Token]: return None - def get_argument_definitions_from_line(line: int) -> List[ArgumentDefinition]: + def get_argument_definitions_from_line( + line: int, + ) -> List[ArgumentDefinition]: keyword_node = next((k for k in keywords_nodes if k.lineno == line), None) if keyword_node is None: return [] - arguments_node = next((n for n in ast.walk(keyword_node) if isinstance(n, Arguments)), None) + arguments_node = next( + (n for n in ast.walk(keyword_node) if isinstance(n, Arguments)), + None, + ) if arguments_node is None: return [] @@ -2613,7 +2714,7 @@ def _create_handler(self, kw: Any) -> Any: self.current_kw = kw try: handler = super()._create_handler(kw) - setattr(handler, "errors", None) + handler.errors = None except DataError as e: err = Error( message=str(e), @@ -2627,7 +2728,7 @@ def _create_handler(self, kw: Any) -> Any: handler.source = kw.source handler.lineno = kw.lineno - setattr(handler, "errors", [err]) + handler.errors = [err] return handler @@ -2646,7 +2747,7 @@ def _create_handler(self, kw: Any) -> Any: ) def get_kw_errors(kw: Any) -> Any: - r = getattr(kw, "errors") if hasattr(kw, "errors") else None + r = kw.errors if hasattr(kw, "errors") else None if get_robot_version() >= (7, 0) and kw.error: if not r: r = [] diff --git a/packages/robot/src/robotcode/robot/utils/ast.py b/packages/robot/src/robotcode/robot/utils/ast.py index 15de4b97e..e8accc914 100644 --- a/packages/robot/src/robotcode/robot/utils/ast.py +++ b/packages/robot/src/robotcode/robot/utils/ast.py @@ -37,10 +37,7 @@ def iter_nodes(node: ast.AST, descendants: bool = True) -> Iterator[ast.AST]: def range_from_token(token: Token) -> Range: return Range( start=Position(line=token.lineno - 1, character=token.col_offset), - end=Position( - line=token.lineno - 1, - character=token.end_col_offset, - ), + end=Position(line=token.lineno - 1, character=token.end_col_offset), ) @@ -105,12 +102,18 @@ def _get_non_data_range_from_node( None, ) if start_token is not None and end_token is not None: - return Range(start=range_from_token(start_token).start, end=range_from_token(end_token).end) + return Range( + start=range_from_token(start_token).start, + end=range_from_token(end_token).end, + ) return None def range_from_node( - node: ast.AST, skip_non_data: bool = False, only_start: bool = False, allow_comments: bool = False + node: ast.AST, + skip_non_data: bool = False, + only_start: bool = False, + allow_comments: bool = False, ) -> Range: if skip_non_data: if isinstance(node, Statement) and node.tokens: @@ -223,7 +226,11 @@ def _tokenize_no_variables(token: Token) -> Iterator[Token]: def tokenize_variables( - token: Token, identifiers: str = "$@&%", ignore_errors: bool = False, *, extra_types: Optional[Set[str]] = None + token: Token, + identifiers: str = "$@&%", + ignore_errors: bool = False, + *, + extra_types: Optional[Set[str]] = None, ) -> Iterator[Token]: if token.type not in { *Token.ALLOW_VARIABLES, @@ -273,7 +280,9 @@ def _tokenize_variables(token: Token, variables: Any) -> Iterator[Token]: yield Token(token.type, after, lineno, col_offset) -def iter_over_keyword_names_and_owners(full_name: str) -> Iterator[Tuple[Optional[str], ...]]: +def iter_over_keyword_names_and_owners( + full_name: str, +) -> Iterator[Tuple[Optional[str], ...]]: yield None, full_name tokens = full_name.split(".") @@ -293,7 +302,12 @@ def strip_variable_token(token: Token) -> Token: stripped_value = value.lstrip() stripped_offset = len(value) - len(stripped_value) - return Token(token.type, stripped_value.rstrip(), token.lineno, token.col_offset + 2 + stripped_offset) + return Token( + token.type, + stripped_value.rstrip(), + token.lineno, + token.col_offset + 2 + stripped_offset, + ) return token diff --git a/packages/robot/src/robotcode/robot/utils/markdownformatter.py b/packages/robot/src/robotcode/robot/utils/markdownformatter.py index e5e66b82e..474718145 100644 --- a/packages/robot/src/robotcode/robot/utils/markdownformatter.py +++ b/packages/robot/src/robotcode/robot/utils/markdownformatter.py @@ -150,7 +150,7 @@ def format_link(self, text: str) -> str: return "".join(f(t) for f, t in zip(formatters, tokens)) def _format_link(self, text: str) -> str: - link, content = [t.strip() for t in text.split("|", 1)] + link, content = (t.strip() for t in text.split("|", 1)) if self._is_image(content): content = self._get_image(content, link) elif self._is_image(link): @@ -171,7 +171,7 @@ def _remove_link(self, text: str) -> str: if "|" not in text: return text - link, content = [t.strip() for t in text.split("|", 1)] + link, content = (t.strip() for t in text.split("|", 1)) if self._is_image(content): return self._get_image(content, link) diff --git a/packages/robot/src/robotcode/robot/utils/robot_path.py b/packages/robot/src/robotcode/robot/utils/robot_path.py index 66a25a96d..921fd8650 100644 --- a/packages/robot/src/robotcode/robot/utils/robot_path.py +++ b/packages/robot/src/robotcode/robot/utils/robot_path.py @@ -28,7 +28,11 @@ def find_file_ex( default = file_type or "File" file_type = ( - {"Library": "Test library", "Variables": "Variable file", "Resource": "Resource file"}.get(file_type, default) + { + "Library": "Test library", + "Variables": "Variable file", + "Resource": "Resource file", + }.get(file_type, default) if file_type else default ) diff --git a/packages/robot/src/robotcode/robot/utils/visitor.py b/packages/robot/src/robotcode/robot/utils/visitor.py index b5b5c522f..2e2be4b51 100644 --- a/packages/robot/src/robotcode/robot/utils/visitor.py +++ b/packages/robot/src/robotcode/robot/utils/visitor.py @@ -1,7 +1,16 @@ import ast from abc import ABC from collections import defaultdict -from typing import Any, AsyncIterator, Callable, Dict, Iterator, Optional, Type, Union +from typing import ( + Any, + AsyncIterator, + Callable, + Dict, + Iterator, + Optional, + Type, + Union, +) from robot.parsing.model.statements import Statement diff --git a/packages/runner/src/robotcode/runner/cli/discover/discover.py b/packages/runner/src/robotcode/runner/cli/discover/discover.py index 7060da04a..ef15eb3d5 100644 --- a/packages/runner/src/robotcode/runner/cli/discover/discover.py +++ b/packages/runner/src/robotcode/runner/cli/discover/discover.py @@ -6,7 +6,16 @@ from dataclasses import dataclass from io import IOBase from pathlib import Path -from typing import Any, Dict, Iterable, List, MutableMapping, Optional, Tuple, Union +from typing import ( + Any, + Dict, + Iterable, + List, + MutableMapping, + Optional, + Tuple, + Union, +) import click import robot.running.model as running_model @@ -29,7 +38,12 @@ from robotcode.core.uri import Uri from robotcode.core.utils.cli import show_hidden_arguments from robotcode.core.utils.dataclasses import from_json -from robotcode.plugin import Application, OutputFormat, UnknownError, pass_application +from robotcode.plugin import ( + Application, + OutputFormat, + UnknownError, + pass_application, +) from robotcode.plugin.click_helper.types import add_options from robotcode.robot.utils import get_robot_version @@ -59,7 +73,7 @@ def _patch() -> None: TestDefaults, ) else: - from robot.running.builder.settings import ( # pyright: ignore[reportMissingImports] + from robot.running.builder.settings import ( # pyright: ignore[reportMissingImports] Defaults as TestDefaults, ) @@ -421,7 +435,11 @@ def add_diagnostic( text=match.group("message").strip(), ) elif match := RE_PARSING_FAILED_MATCHER.match(message.message): - add_diagnostic(message, match.group("file"), text=match.group("message").strip()) + add_diagnostic( + message, + match.group("file"), + text=match.group("message").strip(), + ) else: add_diagnostic(message) @@ -513,10 +531,7 @@ def handle_options( @discover.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `robot` help.', ) @@ -585,10 +600,7 @@ def print(item: TestItem, indent: int = 0) -> Iterable[str]: @discover.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `robot` help.', ) @@ -631,14 +643,18 @@ def tests( ``` """ - suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args) + _suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args) if collector.all.children: if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT: def print(items: List[TestItem]) -> Iterable[str]: for item in items: - yield click.style(f"{item.longname}", bold=True, fg="green" if show_tags else None) + yield click.style( + f"{item.longname}", + bold=True, + fg="green" if show_tags else None, + ) yield click.style( f" ({item.source if full_paths else item.rel_source}" f":{item.range.start.line + 1 if item.range is not None else 1}){os.linesep}" @@ -655,10 +671,7 @@ def print(items: List[TestItem]) -> Iterable[str]: @discover.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `robot` help.', ) @@ -685,7 +698,7 @@ def suites( ``` """ - suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args) + _suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args) if collector.all.children: if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT: @@ -707,10 +720,7 @@ class TagsResult: @discover.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `robot` help.', ) @@ -769,7 +779,11 @@ def tags( def print(tags: Dict[str, List[TestItem]]) -> Iterable[str]: for tag, items in sorted(tags.items()): - yield click.style(f"{tag}{os.linesep}", bold=show_tests, fg="green" if show_tests else None) + yield click.style( + f"{tag}{os.linesep}", + bold=show_tests, + fg="green" if show_tests else None, + ) if show_tests: for t in items: yield click.style(f" {t.longname}", bold=True) + click.style( @@ -798,13 +812,9 @@ class Info: system_version: str -@discover.command( - add_help_option=True, -) +@discover.command(add_help_option=True) @pass_application -def info( - app: Application, -) -> None: +def info(app: Application) -> None: """\ Shows some informations about the current *robot* environment. diff --git a/packages/runner/src/robotcode/runner/cli/libdoc.py b/packages/runner/src/robotcode/runner/cli/libdoc.py index 1c42ee0d2..d5d03a947 100644 --- a/packages/runner/src/robotcode/runner/cli/libdoc.py +++ b/packages/runner/src/robotcode/runner/cli/libdoc.py @@ -28,7 +28,7 @@ def parse_arguments(self, cli_args: Any) -> Any: raise Information( "Dry run, not executing any commands. " f"Would execute libdoc with the following options and arguments:\n" - f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()) ,*arguments))}' + f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()), *arguments))}' ) return options, arguments @@ -41,10 +41,7 @@ def main(self, arguments: Any, **options: Any) -> Any: @click.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see the `libdoc` help.', ) @@ -56,10 +53,7 @@ def main(self, arguments: Any, **options: Any) -> Any: ) @click.argument("robot_options_and_args", nargs=-1, type=click.Path()) @pass_application -def libdoc( - app: Application, - robot_options_and_args: Tuple[str, ...], -) -> None: +def libdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None: """Runs `libdoc` with the selected configuration, profiles, options and arguments. The options and arguments are passed to `libdoc` as is. diff --git a/packages/runner/src/robotcode/runner/cli/rebot.py b/packages/runner/src/robotcode/runner/cli/rebot.py index f6b31a549..59de17381 100644 --- a/packages/runner/src/robotcode/runner/cli/rebot.py +++ b/packages/runner/src/robotcode/runner/cli/rebot.py @@ -28,7 +28,7 @@ def parse_arguments(self, cli_args: Any) -> Any: raise Information( "Dry run, not executing any commands. " f"Would execute libdoc with the following options and arguments:\n" - f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()) ,*arguments))}' + f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()), *arguments))}' ) return options, arguments @@ -41,10 +41,7 @@ def main(self, datasources: Any, **options: Any) -> Any: @click.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `rebot` help.', ) @@ -56,10 +53,7 @@ def main(self, datasources: Any, **options: Any) -> Any: ) @click.argument("robot_options_and_args", nargs=-1, type=click.Path()) @pass_application -def rebot( - app: Application, - robot_options_and_args: Tuple[str, ...], -) -> None: +def rebot(app: Application, robot_options_and_args: Tuple[str, ...]) -> None: """Runs `rebot` with the selected configuration, profiles, options and arguments. The options and arguments are passed to `rebot` as is. diff --git a/packages/runner/src/robotcode/runner/cli/robot.py b/packages/runner/src/robotcode/runner/cli/robot.py index 8f46be147..0a6abcb81 100644 --- a/packages/runner/src/robotcode/runner/cli/robot.py +++ b/packages/runner/src/robotcode/runner/cli/robot.py @@ -18,7 +18,13 @@ class RobotFrameworkEx(RobotFramework): - def __init__(self, app: Application, paths: List[str], dry: bool, root_folder: Optional[Path]) -> None: + def __init__( + self, + app: Application, + paths: List[str], + dry: bool, + root_folder: Optional[Path], + ) -> None: super().__init__() self.app = app self.paths = paths @@ -51,7 +57,7 @@ def parse_arguments(self, cli_args: Any) -> Any: raise Information( "Dry run, not executing any commands. " f"Would execute robot with the following options and arguments:\n" - f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()) ,*arguments))}' + f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()), *arguments))}' ) return options, arguments @@ -60,7 +66,12 @@ def parse_arguments(self, cli_args: Any) -> Any: # mypy: disable-error-code="arg-type" ROBOT_OPTIONS: Set[click.Command] = { - click.option("--by-longname", type=str, multiple=True, help="Select tests/tasks or suites by longname."), + click.option( + "--by-longname", + type=str, + multiple=True, + help="Select tests/tasks or suites by longname.", + ), click.option( "--exclude-by-longname", type=str, @@ -108,7 +119,10 @@ def handle_robot_options( if by_longname: sep = ";" if any(True for l in by_longname if ":" in l) else ":" - cmd_options += ("--prerunmodifier", f"robotcode.modifiers.ByLongName{sep}{sep.join(by_longname)}") + cmd_options += ( + "--prerunmodifier", + f"robotcode.modifiers.ByLongName{sep}{sep.join(by_longname)}", + ) if exclude_by_longname: sep = ";" if any(True for l in exclude_by_longname if ":" in l) else ":" @@ -133,10 +147,7 @@ def handle_robot_options( @click.command( cls=AliasedCommand, aliases=["run"], - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `robot` help.', ) @@ -179,9 +190,6 @@ def robot( else [profile.paths], app.config.dry, root_folder, - ).execute_cli( - (*cmd_options, *robot_options_and_args), - exit=False, - ), + ).execute_cli((*cmd_options, *robot_options_and_args), exit=False), ) ) diff --git a/packages/runner/src/robotcode/runner/cli/testdoc.py b/packages/runner/src/robotcode/runner/cli/testdoc.py index ef6ba67f0..64be24801 100644 --- a/packages/runner/src/robotcode/runner/cli/testdoc.py +++ b/packages/runner/src/robotcode/runner/cli/testdoc.py @@ -28,7 +28,7 @@ def parse_arguments(self, cli_args: Any) -> Any: raise Information( "Dry run, not executing any commands. " f"Would execute testdoc with the followingoptions and arguments:\n" - f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()) ,*arguments))}' + f'{line_end.join((*(f"{k} = {v!r}" for k, v in options.items()), *arguments))}' ) return options, arguments @@ -41,10 +41,7 @@ def main(self, arguments: Any, **options: Any) -> Any: @click.command( - context_settings={ - "allow_extra_args": True, - "ignore_unknown_options": True, - }, + context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, add_help_option=True, epilog='Use "-- --help" to see `testdoc` help.', ) @@ -56,10 +53,7 @@ def main(self, arguments: Any, **options: Any) -> Any: ) @click.argument("robot_options_and_args", nargs=-1, type=click.Path()) @pass_application -def testdoc( - app: Application, - robot_options_and_args: Tuple[str, ...], -) -> None: +def testdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None: """Runs `testdoc` with the selected configuration, profiles, options and arguments. The options and arguments are passed to `testdoc` as is. @@ -103,5 +97,8 @@ def testdoc( ) app.exit( - cast(int, TestDocEx(app.config.dry, root_folder).execute_cli((*options, *robot_options_and_args), exit=False)) + cast( + int, + TestDocEx(app.config.dry, root_folder).execute_cli((*options, *robot_options_and_args), exit=False), + ) ) diff --git a/pyproject.toml b/pyproject.toml index 80a96c99a..c7d61c819 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,7 +163,7 @@ fail_under = 40 line-length = 120 target-version = "py38" extend-exclude = ["bundled/libs", ".hatch"] -ignore = ["E741", "N805", "N999", "RUF012", "RUF006"] +ignore = ["E741", "N805", "N999", "RUF012", "RUF006", "ISC001"] select = [ "E", "F", @@ -203,7 +203,6 @@ select = [ # "TID" ] - [tool.ruff.per-file-ignores] #"__init__.py" = ["F401"] @@ -246,7 +245,7 @@ mypy_path = [ ] explicit_package_bases = true namespace_packages = true -allow_untyped_calls = true +# allow_untyped_calls = true [[tool.mypy.overrides]] diff --git a/scripts/deploy_docs.py b/scripts/deploy_docs.py index d01ebaef7..f75d647b4 100644 --- a/scripts/deploy_docs.py +++ b/scripts/deploy_docs.py @@ -31,6 +31,7 @@ def main() -> None: "mike deploy --push --update-aliases " f'--title "v{version.major}.{version.minor}.x ({alias})" {version.major}.{version.minor} {alias}', shell=True, + check=False, ).check_returncode() diff --git a/scripts/extract_release_notes.py b/scripts/extract_release_notes.py index 668d1897f..10ad6f536 100644 --- a/scripts/extract_release_notes.py +++ b/scripts/extract_release_notes.py @@ -24,7 +24,10 @@ def main() -> None: changelog = Path("CHANGELOG.md").read_text() - regex = re.compile(rf"^\#\#\s*\[({version})\][^\n]*?\n(?P.*?)^\#\#\s+", re.MULTILINE | re.DOTALL) + regex = re.compile( + rf"^\#\#\s*\[({version})\][^\n]*?\n(?P.*?)^\#\#\s+", + re.MULTILINE | re.DOTALL, + ) for match in regex.finditer(changelog): print(match.group("text").strip()) diff --git a/scripts/generate_rf_options.py b/scripts/generate_rf_options.py index 0ce39c7e7..08f198838 100644 --- a/scripts/generate_rf_options.py +++ b/scripts/generate_rf_options.py @@ -126,8 +126,14 @@ def create_desc(v: Dict[str, str], extra: bool = False) -> str: return result - def get_type(name: str, value: Any, option: Dict[str, str], is_flag: bool = False, extra: bool = False) -> str: - if not option.get("param", None): + def get_type( + name: str, + value: Any, + option: Dict[str, str], + is_flag: bool = False, + extra: bool = False, + ) -> str: + if not option.get("param"): if is_flag: return "Union[bool, Flag, None]" @@ -145,8 +151,8 @@ def get_type(name: str, value: Any, option: Dict[str, str], is_flag: bool = Fals if base_type == "str": base_type = "Union[str, StringExpression]" - if param := option.get("param", None): - if len((param_splitted := param.split("|"))) > 1: + if param := option.get("param"): + if len(param_splitted := param.split("|")) > 1: has_literal = [x for x in param_splitted if ":" not in x] has_pattern = [x for x in param_splitted if ":" in x] base_type = ( @@ -165,10 +171,10 @@ def get_type(name: str, value: Any, option: Dict[str, str], is_flag: bool = Fals + ("]" if has_literal and has_pattern or len(has_pattern) > 1 else "") ) - elif len((param_splitted := param.split(":"))) > 1: + elif len(param_splitted := param.split(":")) > 1: return f"Optional[Dict[str, {base_type}]]" - if option.get("star", None): + if option.get("star"): return f"Optional[List[{base_type}]]" return f"Optional[{base_type}]" @@ -189,7 +195,7 @@ def build_class_fields(output: List[str], opts: Dict[str, Any], extend: bool = F result.update({k: v}) name = ("extend_" if extend else "") + to_snake_case( - name_corrections.get(long_name, None) or internal_options[long_name]["option"] + name_corrections.get(long_name) or internal_options[long_name]["option"] ) output.append( f" {name}" @@ -245,7 +251,14 @@ def build_class_fields(output: List[str], opts: Dict[str, Any], extend: bool = F output.append("class RobotExtraOptions(BaseOptions):") output.append(' """Extra options for _robot_ command."""') output.append("") -generate(output, ROBOT_USAGE, RobotSettings._extra_cli_opts, extra_cmd_options, extra=True, tool="rebot") +generate( + output, + ROBOT_USAGE, + RobotSettings._extra_cli_opts, + extra_cmd_options, + extra=True, + tool="rebot", +) output.append("") output.append("") @@ -253,7 +266,14 @@ def build_class_fields(output: List[str], opts: Dict[str, Any], extend: bool = F output.append("class RebotOptions(BaseOptions):") output.append(' """Options for _rebot_ command."""') output.append("") -extra_cmd_options = generate(output, REBOT_USAGE, RebotSettings._extra_cli_opts, None, extra=False, tool="rebot") +extra_cmd_options = generate( + output, + REBOT_USAGE, + RebotSettings._extra_cli_opts, + None, + extra=False, + tool="rebot", +) libdoc_options: Dict[str, Tuple[str, Any]] = { @@ -280,7 +300,14 @@ def build_class_fields(output: List[str], opts: Dict[str, Any], extend: bool = F output.append("class LibDocExtraOptions(BaseOptions):") output.append(' """Extra options for _libdoc_ command."""') output.append("") -generate(output, LIBDOC_USAGE, libdoc_options, extra_cmd_options, extra=True, tool="libdoc") +generate( + output, + LIBDOC_USAGE, + libdoc_options, + extra_cmd_options, + extra=True, + tool="libdoc", +) testdoc_options: Dict[str, Tuple[str, Any]] = { @@ -303,7 +330,14 @@ def build_class_fields(output: List[str], opts: Dict[str, Any], extend: bool = F output.append("class TestDocExtraOptions(BaseOptions):") output.append(' """Extra options for _testdoc_ command."""') output.append("") -generate(output, TESTDOC_USAGE, testdoc_options, extra_cmd_options, extra=True, tool="testdoc") +generate( + output, + TESTDOC_USAGE, + testdoc_options, + extra_cmd_options, + extra=True, + tool="testdoc", +) model_file = Path("packages/robot/src/robotcode/robot/config/model.py") original_lines = model_file.read_text().splitlines() diff --git a/scripts/install_bundled_editable.py b/scripts/install_bundled_editable.py index a71caf191..1d81d0a7a 100644 --- a/scripts/install_bundled_editable.py +++ b/scripts/install_bundled_editable.py @@ -14,6 +14,7 @@ def main() -> None: "pip --disable-pip-version-check install -U -t ./bundled/libs --no-cache-dir --implementation py " "--only-binary=:all: --no-binary=:none: -r ./bundled_requirements.txt", shell=True, + check=False, ).check_returncode() packages = [f"-e {path}" for path in Path("./packages").iterdir() if (path / "pyproject.toml").exists()] @@ -22,6 +23,7 @@ def main() -> None: "pip --disable-pip-version-check " f"install -U -t ./bundled/libs --no-cache-dir --implementation py --no-deps {' '.join(packages)} -e .", shell=True, + check=False, ).check_returncode() diff --git a/scripts/install_packages.py b/scripts/install_packages.py index 198c36436..7318600bf 100644 --- a/scripts/install_packages.py +++ b/scripts/install_packages.py @@ -10,6 +10,7 @@ def main() -> None: run( "pip --disable-pip-version-check install -U -r ./bundled_requirements.txt", shell=True, + check=False, ).check_returncode() packages = [f"-e {path}" for path in Path("./packages").iterdir() if (path / "pyproject.toml").exists()] @@ -17,6 +18,7 @@ def main() -> None: run( f"pip --disable-pip-version-check install --no-deps -U {' '.join(packages)}", shell=True, + check=False, ).check_returncode() diff --git a/scripts/package.py b/scripts/package.py index 5c1c8d5b3..116bcbabe 100644 --- a/scripts/package.py +++ b/scripts/package.py @@ -27,9 +27,14 @@ def main() -> None: packages = [f"{path}" for path in Path("./packages").iterdir() if (path / "pyproject.toml").exists()] for package in packages: - run(f"hatch -e build build {dist_path}", shell=True, cwd=package).check_returncode() + run( + f"hatch -e build build {dist_path}", + shell=True, + cwd=package, + check=False, + ).check_returncode() - run(f"hatch -e build build {dist_path}", shell=True).check_returncode() + run(f"hatch -e build build {dist_path}", shell=True, check=False).check_returncode() shutil.rmtree("./bundled/libs", ignore_errors=True) @@ -37,16 +42,20 @@ def main() -> None: "pip --disable-pip-version-check install -U -t ./bundled/libs --no-cache-dir --implementation py " "--only-binary=:all: --no-binary=:none: -r ./bundled_requirements.txt", shell=True, + check=False, ).check_returncode() run( "pip --disable-pip-version-check " f"install -U -t ./bundled/libs --no-cache-dir --implementation py --no-deps {' '.join(packages)} .", shell=True, + check=False, ).check_returncode() run( - f"npx vsce package {'--pre-release' if get_version().prerelease else ''} -o ./dist", shell=True + f"npx vsce package {'--pre-release' if get_version().prerelease else ''} -o ./dist", + shell=True, + check=False, ).check_returncode() diff --git a/scripts/publish.py b/scripts/publish.py index 2b561bba9..e38a02f71 100644 --- a/scripts/publish.py +++ b/scripts/publish.py @@ -41,8 +41,18 @@ def main() -> None: vsix_path = Path(dist_path, f"robotcode-{current_version}.vsix") - run("npx vsce publish", f"npx vsce publish -i {vsix_path}", shell=True, timeout=600) - run("npx ovsx publish", f"npx ovsx publish {vsix_path}", shell=True, timeout=600) + run( + "npx vsce publish", + f"npx vsce publish -i {vsix_path}", + shell=True, + timeout=600, + ) + run( + "npx ovsx publish", + f"npx ovsx publish {vsix_path}", + shell=True, + timeout=600, + ) run( "hatch publish", diff --git a/scripts/update_git_versions.py b/scripts/update_git_versions.py index 5bef54c28..d34f4b3db 100644 --- a/scripts/update_git_versions.py +++ b/scripts/update_git_versions.py @@ -38,7 +38,10 @@ def main() -> None: replace_in_file( Path("package.json"), - re.compile(r"""(\"version\"\s*:\s*['"])([0-9]+\.[0-9]+\.[0-9]+.*)(['"])""", re.MULTILINE), + re.compile( + r"""(\"version\"\s*:\s*['"])([0-9]+\.[0-9]+\.[0-9]+.*)(['"])""", + re.MULTILINE, + ), rf"\g<1>{version or ''}\g<3>", ) diff --git a/src/robotcode/cli/__init__.py b/src/robotcode/cli/__init__.py index 6727c82ef..c6fdb1f88 100644 --- a/src/robotcode/cli/__init__.py +++ b/src/robotcode/cli/__init__.py @@ -5,7 +5,12 @@ import click from robotcode.core.utils.cli import show_hidden_arguments from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.plugin import Application, ColoredOutput, OutputFormat, pass_application +from robotcode.plugin import ( + Application, + ColoredOutput, + OutputFormat, + pass_application, +) from robotcode.plugin.click_helper.aliases import AliasedGroup from robotcode.plugin.click_helper.types import EnumChoice from robotcode.plugin.manager import PluginManager @@ -52,7 +57,13 @@ help="Set the output format.", show_default=True, ) -@click.option("-d", "--dry", is_flag=True, show_envvar=True, help="Dry run, do not execute any commands.") +@click.option( + "-d", + "--dry", + is_flag=True, + show_envvar=True, + help="Dry run, do not execute any commands.", +) @click.option( "--color / --no-color", "color", @@ -74,12 +85,7 @@ help="Enables verbose mode.", show_envvar=True, ) -@click.option( - "--log", - is_flag=True, - help="Enables logging.", - show_envvar=True, -) +@click.option("--log", is_flag=True, help="Enables logging.", show_envvar=True) @click.option( "--log-level", type=click.Choice(["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]), @@ -90,7 +96,13 @@ ) @click.option( "--log-filename", - type=click.Path(file_okay=True, dir_okay=False, writable=True, exists=False, path_type=str), + type=click.Path( + file_okay=True, + dir_okay=False, + writable=True, + exists=False, + path_type=str, + ), help="Write log output to a file instead to console.", default=None, show_default=True, @@ -195,10 +207,17 @@ def robotcode( if log_calls: LoggingDescriptor.set_call_tracing(True) - logging.basicConfig(level=log_level, format="%(name)s:%(levelname)s: %(message)s", filename=log_filename) + logging.basicConfig( + level=log_level, + format="%(name)s:%(levelname)s: %(message)s", + filename=log_filename, + ) if debugpy: - from robotcode.core.utils.debugpy import start_debugpy, wait_for_debugpy_connected + from robotcode.core.utils.debugpy import ( + start_debugpy, + wait_for_debugpy_connected, + ) app.verbose(f"Try to start a debugpy session on port {debugpy_port}") diff --git a/src/robotcode/cli/commands/config.py b/src/robotcode/cli/commands/config.py index 81ae40e89..0a281f3e8 100644 --- a/src/robotcode/cli/commands/config.py +++ b/src/robotcode/cli/commands/config.py @@ -6,35 +6,50 @@ import click from robotcode.core.utils.dataclasses import encode_case_for_field_name -from robotcode.plugin import Application, OutputFormat, UnknownError, pass_application +from robotcode.plugin import ( + Application, + OutputFormat, + UnknownError, + pass_application, +) from robotcode.plugin.manager import PluginManager from robotcode.robot.config.loader import ( DiscoverdBy, find_project_root, load_robot_config_from_path, ) -from robotcode.robot.config.model import LibDocProfile, RebotProfile, RobotConfig, RobotProfile, TestDocProfile +from robotcode.robot.config.model import ( + LibDocProfile, + RebotProfile, + RobotConfig, + RobotProfile, + TestDocProfile, +) from robotcode.robot.config.utils import get_config_files -@click.group( - invoke_without_command=False, -) +@click.group(invoke_without_command=False) def config() -> None: """Shows information about the configuration.""" @config.command @click.option( - "-s", "--single", "single", is_flag=True, default=False, help="Shows single files, not the combined config." + "-s", + "--single", + "single", + is_flag=True, + default=False, + help="Shows single files, not the combined config.", +) +@click.argument( + "paths", + type=click.Path(exists=True, path_type=Path), + nargs=-1, + required=False, ) -@click.argument("paths", type=click.Path(exists=True, path_type=Path), nargs=-1, required=False) @pass_application -def show( - app: Application, - single: bool, - paths: List[Path], -) -> None: +def show(app: Application, single: bool, paths: List[Path]) -> None: """\ Shows the current configuration. @@ -56,20 +71,33 @@ def show( for file, _ in config_files: config = load_robot_config_from_path(file) click.secho(f"File: {file}") - app.print_data(config, remove_defaults=True, default_output_format=OutputFormat.TOML) + app.print_data( + config, + remove_defaults=True, + default_output_format=OutputFormat.TOML, + ) return config = load_robot_config_from_path(*config_files) - app.print_data(config, remove_defaults=True, default_output_format=OutputFormat.TOML) + app.print_data( + config, + remove_defaults=True, + default_output_format=OutputFormat.TOML, + ) except (TypeError, ValueError, OSError) as e: raise UnknownError(str(e)) from e @config.command -@click.argument("paths", type=click.Path(exists=True, path_type=Path), nargs=-1, required=False) +@click.argument( + "paths", + type=click.Path(exists=True, path_type=Path), + nargs=-1, + required=False, +) @click.argument("user", type=bool, default=False) @pass_application def files(app: Application, paths: List[Path], user: bool = False) -> None: @@ -90,9 +118,7 @@ def files(app: Application, paths: List[Path], user: bool = False) -> None: try: config_files, _, discovered_by = get_config_files(paths, app.config.config_files, verbose_callback=app.verbose) - result: Dict[str, Any] = { - "files": [{"path": str(file), "type": type} for file, type in config_files], - } + result: Dict[str, Any] = {"files": [{"path": str(file), "type": type} for file, type in config_files]} messages = [] if discovered_by == DiscoverdBy.NOT_FOUND: @@ -113,12 +139,14 @@ def files(app: Application, paths: List[Path], user: bool = False) -> None: @config.command -@click.argument("paths", type=click.Path(exists=True, path_type=Path), nargs=-1, required=False) +@click.argument( + "paths", + type=click.Path(exists=True, path_type=Path), + nargs=-1, + required=False, +) @pass_application -def root( - app: Application, - paths: List[Path], -) -> None: +def root(app: Application, paths: List[Path]) -> None: """\ Searches for the root folder of the project and prints them. @@ -139,7 +167,10 @@ def root( raise click.ClickException("Cannot detect root folder. 😥") result: Dict[str, Any] = { - "root": {"path": str(root_folder) if root_folder is not None else None, "discoverdBy": discovered_by} + "root": { + "path": str(root_folder) if root_folder is not None else None, + "discoverdBy": discovered_by, + } } messages = [] @@ -292,7 +323,11 @@ def desc(app: Application, name: Optional[List[str]] = None) -> None: app.print_data( { "descriptions": [ - {"name": field, "type": value["type"], "description": value["description"]} + { + "name": field, + "type": value["type"], + "description": value["description"], + } for field, value in config_fields ] } diff --git a/src/robotcode/cli/commands/profiles.py b/src/robotcode/cli/commands/profiles.py index 20db90a64..7200c4fbf 100644 --- a/src/robotcode/cli/commands/profiles.py +++ b/src/robotcode/cli/commands/profiles.py @@ -2,7 +2,12 @@ from typing import Any, Dict, List import click -from robotcode.plugin import Application, OutputFormat, UnknownError, pass_application +from robotcode.plugin import ( + Application, + OutputFormat, + UnknownError, + pass_application, +) from robotcode.robot.config.loader import ( DiscoverdBy, load_robot_config_from_path, @@ -11,24 +16,28 @@ from robotcode.robot.config.utils import get_config_files -@click.group( - invoke_without_command=False, -) +@click.group(invoke_without_command=False) def profiles() -> None: """Shows information on defined profiles.""" @profiles.command @click.option( - "-n", "--no-evaluate", "no_evaluate", is_flag=True, default=False, help="Don't evaluate expressions in the profile." + "-n", + "--no-evaluate", + "no_evaluate", + is_flag=True, + default=False, + help="Don't evaluate expressions in the profile.", +) +@click.argument( + "paths", + type=click.Path(exists=True, path_type=Path), + nargs=-1, + required=False, ) -@click.argument("paths", type=click.Path(exists=True, path_type=Path), nargs=-1, required=False) @pass_application -def show( - app: Application, - no_evaluate: bool, - paths: List[Path], -) -> None: +def show(app: Application, no_evaluate: bool, paths: List[Path]) -> None: """Shows the given profile configuration.""" try: config_files, _, _ = get_config_files(paths, app.config.config_files, verbose_callback=app.verbose) @@ -40,27 +49,29 @@ def show( if not no_evaluate: config = config.evaluated() - app.print_data(config, remove_defaults=True, default_output_format=OutputFormat.TOML) + app.print_data( + config, + remove_defaults=True, + default_output_format=OutputFormat.TOML, + ) except (TypeError, ValueError, OSError) as e: raise UnknownError(str(e)) from e @profiles.command -@click.argument("paths", type=click.Path(exists=True, path_type=Path), nargs=-1, required=False) +@click.argument( + "paths", + type=click.Path(exists=True, path_type=Path), + nargs=-1, + required=False, +) @pass_application -def list( - app: Application, - paths: List[Path], -) -> None: +def list(app: Application, paths: List[Path]) -> None: """Lists the defined profiles in the current configuration.""" try: - config_files, _, discovered_by = get_config_files( - paths, - app.config.config_files, - verbose_callback=app.verbose, - ) + config_files, _, discovered_by = get_config_files(paths, app.config.config_files, verbose_callback=app.verbose) config = load_robot_config_from_path(*config_files) selected_profiles = [ @@ -102,15 +113,21 @@ def check_enabled(name: str, profile: RobotProfile) -> bool: v[k] = " ".join(lines[:1]) + (" ..." if len(lines) > 1 else "") header = "" - max_name = max(0, len("Name"), *(len(profile["name"]) for profile in result["profiles"])) + max_name = max( + 0, + len("Name"), + *(len(profile["name"]) for profile in result["profiles"]), + ) max_description = max( - 0, len("Description"), *(len(profile["description"]) for profile in result["profiles"]) + 0, + len("Description"), + *(len(profile["description"]) for profile in result["profiles"]), ) header += ( - f'| Active | Selected | Enabled | Name{(max_name-len("Name"))*" "} ' - f'| Description{(max_description-len("Description"))*" "} |\n' + f'| Active | Selected | Enabled | Name{(max_name - len("Name")) * " "} ' + f'| Description{(max_description - len("Description")) * " "} |\n' ) - header += f"|:------:|:--------:|:-------:|:{max_name*'-'}-|:{max_description*'-'}-|\n" + header += f"|:------:|:--------:|:-------:|:{max_name * '-'}-|:{max_description * '-'}-|\n" for selected, enabled, name, description in ( (v["selected"], v["enabled"], v["name"], v["description"]) for v in result["profiles"] ): @@ -118,8 +135,8 @@ def check_enabled(name: str, profile: RobotProfile) -> bool: f'| {"*" if selected and enabled else " "} ' f'| {"*" if selected else " "} ' f'| {"*" if enabled else " "} ' - f'| {name}{(max_name-len(name))*" "} ' - f'| {description if description else ""}{(max_description-len(description))*" "} |\n' + f'| {name}{(max_name - len(name)) * " "} ' + f'| {description if description else ""}{(max_description - len(description)) * " "} |\n' ) app.echo_as_markdown(header) diff --git a/tests/conftest.py b/tests/conftest.py index e9c0af23f..9e2647813 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="no-untyped-def" +# mypy: disable-error-code="no-untyped-def,no-untyped-call" # this is a modified version of [pytest-regtest](https://gitlab.com/uweschmitt/pytest-regtest) # which is licensed under the MIT license # author: Uwe Schmitt (https://gitlab.com/uweschmitt) @@ -12,6 +12,7 @@ import tempfile from hashlib import sha512 from io import StringIO +from pathlib import Path import pytest from _pytest._code.code import ExceptionInfo, TerminalRepr @@ -54,8 +55,14 @@ def _std_replacements(request): regexp = os.path.join(tempfile.gettempdir(), "tmp[_a-zA-Z0-9]+") yield regexp, "" - yield os.path.realpath(tempfile.gettempdir()) + os.path.sep, "/" - yield os.path.realpath(tempfile.gettempdir()), "" + yield ( + os.path.realpath(tempfile.gettempdir()) + os.path.sep, + "/", + ) + yield ( + os.path.realpath(tempfile.gettempdir()), + "", + ) if tempfile.tempdir: yield tempfile.tempdir + os.path.sep, "/" yield tempfile.tempdir, "" @@ -150,33 +157,28 @@ def pytest_configure(config): class RegTestFixture: - def __init__(self, request, nodeid): + def __init__(self, request: pytest.FixtureRequest): self.request = request - self.nodeid = nodeid + self.nodeid = request.node.nodeid - self.test_folder = request.fspath.dirname + self.test_folder = request.path.parent self.buffer = StringIO() - self.identifier = None @property - def old_output_file_name(self): - file_name, __, test_function = self.nodeid.partition("::") - file_name = os.path.basename(file_name) + def old_output_file_name(self) -> Path: + name, __, test_function = self.nodeid.partition("::") + file_name = Path(name) test_function = test_function.replace("/", "--") if len(test_function) > 100: test_function = sha512(test_function.encode("utf-8")).hexdigest()[:10] - stem, __ = os.path.splitext(file_name) - if self.identifier is not None: - return stem + "." + test_function + "__" + self.identifier + ".out" - - return stem + "." + test_function + ".out" + return Path(f"{file_name.stem}.{test_function}.out") @property - def output_file_name(self): - file_name, __, test_function = self.nodeid.partition("::") - file_name = os.path.basename(file_name) + def output_file_name(self) -> Path: + name, __, test_function = self.nodeid.partition("::") + file_name = Path(name) for c in "/\\:*\"'?<>|": test_function = test_function.replace(c, "-") @@ -186,24 +188,21 @@ def output_file_name(self): test_function = test_function[:88] + "__" + sha512(test_function.encode("utf-8")).hexdigest()[:10] test_function = test_function.replace(" ", "_") - stem, __ = os.path.splitext(file_name) - if self.identifier is not None: - return stem + "." + test_function + "__" + self.identifier + ".out" - return stem + "." + test_function + ".out" + return Path(f"{file_name.stem}.{test_function}.out") @property - def old_result_file(self): - return os.path.join(self.test_folder, "_regtest_outputs", self.old_output_file_name) + def old_result_file(self) -> Path: + return Path(self.test_folder, "_regtest_outputs", self.old_output_file_name) @property - def result_file(self): - return os.path.join(self.test_folder, "_regtest_outputs", self.output_file_name) + def result_file(self) -> Path: + return Path(self.test_folder, "_regtest_outputs", self.output_file_name) - def write(self, what): + def write(self, what: str) -> None: self.buffer.write(what) - def flush(self): + def flush(self) -> None: pass @property @@ -248,9 +247,7 @@ def __exit__(self, exc_type, exc_value, traceback): @pytest.fixture(scope="session") def regtest(request: pytest.FixtureRequest): - item = request.node - - return RegTestFixture(request, item.nodeid) + return RegTestFixture(request) @pytest.hookimpl(hookwrapper=True) @@ -326,10 +323,10 @@ def handle_regtest_result(regtest, result, xfail): else: result.outcome = "failed" - nodeid = regtest.nodeid + ("" if regtest.identifier is None else "__" + regtest.identifier) + nodeid = regtest.nodeid if Config.nodiff: result.longrepr = CollectErrorRepr( - ["regression test for {} failed\n".format(nodeid)], + [f"regression test for {nodeid} failed\n"], [dict(red=True, bold=True)], ) return @@ -340,7 +337,7 @@ def handle_regtest_result(regtest, result, xfail): tobe = map(repr, tobe) collected = list(difflib.unified_diff(tobe, current, "tobe", "current", lineterm="")) - msg = "\nregression test output differences for {}:\n".format(nodeid) + msg = f"\nregression test output differences for {nodeid}:\n" msg_diff = "> " + "\n> ".join(collected) result.longrepr = CollectErrorRepr([msg, msg_diff + "\n"], [dict(), dict(red=True, bold=True)]) diff --git a/tests/robotcode/core/test_dataclasses.py b/tests/robotcode/core/test_dataclasses.py index e023630b8..598da66b0 100644 --- a/tests/robotcode/core/test_dataclasses.py +++ b/tests/robotcode/core/test_dataclasses.py @@ -77,7 +77,12 @@ WorkspaceSymbolClientCapabilitiesSymbolKindType, WorkspaceSymbolClientCapabilitiesTagSupportType, ) -from robotcode.core.utils.dataclasses import as_json, from_json, to_camel_case, to_snake_case +from robotcode.core.utils.dataclasses import ( + as_json, + from_json, + to_camel_case, + to_snake_case, +) class EnumData(Enum): @@ -155,7 +160,10 @@ def _decode_case(cls, s: str) -> str: @pytest.mark.parametrize( ("expr", "expected"), [ - (ComplexItemWithConfigEncodeCase([], {}), '{"listField": [], "dictField": {}}'), + ( + ComplexItemWithConfigEncodeCase([], {}), + '{"listField": [], "dictField": {}}', + ), ( ComplexItemWithConfigEncodeCase([1, "2", 3], {"a": "hello", 1: True}), '{"listField": [1, "2", 3], "dictField": {"a": "hello", "1": true}}', @@ -202,8 +210,16 @@ def test_encode_with_optional_field_and_none_as_default_value() -> None: ("[]", Union[int, str, List[int]], []), ('"first"', EnumData, EnumData.FIRST), ('"second"', EnumData, EnumData.SECOND), - ('["first", "second"]', List[EnumData], [EnumData.FIRST, EnumData.SECOND]), - ('["first", "second", "ninety"]', List[Union[EnumData, str]], [EnumData.FIRST, EnumData.SECOND, "ninety"]), + ( + '["first", "second"]', + List[EnumData], + [EnumData.FIRST, EnumData.SECOND], + ), + ( + '["first", "second", "ninety"]', + List[Union[EnumData, str]], + [EnumData.FIRST, EnumData.SECOND, "ninety"], + ), ], ) def test_decode_simple(expr: Any, type: Any, expected: str) -> None: @@ -217,8 +233,16 @@ def test_decode_simple(expr: Any, type: Any, expected: str) -> None: ('{"a": 1}', dict, {"a": 1}), ('{"a": 1}', Dict[str, int], {"a": 1}), ('{"a": 1, "b": 2}', Dict[str, int], {"a": 1, "b": 2}), - ('{"a": 1, "b": null}', Dict[str, Union[int, str, None]], {"a": 1, "b": None}), - ('{"a": {}, "b": {"a": 2}}', Dict[str, Dict[str, Any]], {"a": {}, "b": {"a": 2}}), + ( + '{"a": 1, "b": null}', + Dict[str, Union[int, str, None]], + {"a": 1, "b": None}, + ), + ( + '{"a": {}, "b": {"a": 2}}', + Dict[str, Dict[str, Any]], + {"a": {}, "b": {"a": 2}}, + ), ], ) def test_decode_dict(expr: Any, type: Any, expected: str) -> None: @@ -309,13 +333,21 @@ class SimpleItemWithOptionalFields: @pytest.mark.parametrize( ("expr", "type", "expected"), [ - ('{"first": 1}', SimpleItemWithOptionalFields, SimpleItemWithOptionalFields(first=1)), + ( + '{"first": 1}', + SimpleItemWithOptionalFields, + SimpleItemWithOptionalFields(first=1), + ), ( '{"first": 1, "third": "Hello"}', SimpleItemWithOptionalFields, SimpleItemWithOptionalFields(first=1, third="Hello"), ), - ('{"first": 1, "forth": 1.0}', SimpleItemWithOptionalFields, SimpleItemWithOptionalFields(first=1, forth=1.0)), + ( + '{"first": 1, "forth": 1.0}', + SimpleItemWithOptionalFields, + SimpleItemWithOptionalFields(first=1, forth=1.0), + ), ], ) def test_decode_simple_item_with_optional_field(expr: Any, type: Any, expected: str) -> None: @@ -337,8 +369,16 @@ class ComplexItemWithUnionType: @pytest.mark.parametrize( ("expr", "type", "expected"), [ - ('{"a_union_field":{"a":1, "b":2}}', ComplexItemWithUnionType, ComplexItemWithUnionType(SimpleItem(1, 2))), - ('{"a_union_field":{"d":1, "e":2}}', ComplexItemWithUnionType, ComplexItemWithUnionType(SimpleItem1(1, 2))), + ( + '{"a_union_field":{"a":1, "b":2}}', + ComplexItemWithUnionType, + ComplexItemWithUnionType(SimpleItem(1, 2)), + ), + ( + '{"a_union_field":{"d":1, "e":2}}', + ComplexItemWithUnionType, + ComplexItemWithUnionType(SimpleItem1(1, 2)), + ), ( '{"a_union_field":{"d":1, "e":2, "f": 3}}', ComplexItemWithUnionType, @@ -364,13 +404,17 @@ class ComplexItemWithUnionTypeWithSameProperties: def test_decode_with_union_and_some_same_keys() -> None: assert from_json( - '{"a_union_field": {"a": 1, "b":2, "c":3}}', ComplexItemWithUnionTypeWithSameProperties + '{"a_union_field": {"a": 1, "b":2, "c":3}}', + ComplexItemWithUnionTypeWithSameProperties, ) == ComplexItemWithUnionTypeWithSameProperties(SimpleItem2(1, 2, 3)) def test_decode_with_union_and_same_keys_should_raise_typeerror() -> None: with pytest.raises(TypeError): - from_json('{"a_union_field": {"a": 1, "b":2}}', ComplexItemWithUnionTypeWithSameProperties) + from_json( + '{"a_union_field": {"a": 1, "b":2}}', + ComplexItemWithUnionTypeWithSameProperties, + ) def test_decode_with_union_and_no_keys_should_raise_typeerror() -> None: @@ -380,7 +424,10 @@ def test_decode_with_union_and_no_keys_should_raise_typeerror() -> None: def test_decode_with_union_and_no_match_should_raise_typeerror() -> None: with pytest.raises(TypeError): - from_json('{"a_union_field": {"x": 1, "y":2}}', ComplexItemWithUnionTypeWithSameProperties) + from_json( + '{"a_union_field": {"x": 1, "y":2}}', + ComplexItemWithUnionTypeWithSameProperties, + ) @dataclass @@ -394,7 +441,11 @@ class SimpleItem3: ("expr", "type", "expected"), [ ('{"a":1, "b": 2}', (SimpleItem, SimpleItem3), SimpleItem(1, 2)), - ('{"a":1, "b": 2, "c": 3}', (SimpleItem, SimpleItem3), SimpleItem3(1, 2, 3)), + ( + '{"a":1, "b": 2, "c": 3}', + (SimpleItem, SimpleItem3), + SimpleItem3(1, 2, 3), + ), ], ) def test_decode_with_some_same_fields(expr: Any, type: Any, expected: str) -> None: @@ -433,7 +484,8 @@ def test_decode_union_with_simple_and_complex_types(expr: Any, type: Any, expect def test_decode_union_with_unknown_keys_should_raise_typeerror() -> None: with pytest.raises(TypeError): from_json( - '{"a_union_field": {"d":1, "ef":2}}', ComplexItemWithUnionTypeWithSimpleAndComplexTypes + '{"a_union_field": {"d":1, "ef":2}}', + ComplexItemWithUnionTypeWithSimpleAndComplexTypes, ) == ComplexItemWithUnionTypeWithSimpleAndComplexTypes(SimpleItem(1, 2)) @@ -441,12 +493,28 @@ def test_decode_union_with_unknown_keys_should_raise_typeerror() -> None: ("expr", "type", "expected"), [ ('{"a":1, "b":2, "c":3}', SimpleItem, SimpleItem(1, 2)), - ('{"a":1, "b":2, "c":3}', SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields(1, 2)), - ('{"a":1}', SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields(1)), - ("{}", SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields()), + ( + '{"a":1, "b":2, "c":3}', + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(1, 2), + ), + ( + '{"a":1}', + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(1), + ), + ( + "{}", + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(), + ), ("{}", SimpleItemWithNoFields, SimpleItemWithNoFields()), ('{"a": 1}', SimpleItemWithNoFields, SimpleItemWithNoFields()), - ('{"a":1, "b":2, "c": 3}', (SimpleItemWithNoFields, SimpleItem), SimpleItem(1, 2)), + ( + '{"a":1, "b":2, "c": 3}', + (SimpleItemWithNoFields, SimpleItem), + SimpleItem(1, 2), + ), ], ) def test_decode_non_strict_should_work(expr: Any, type: Any, expected: str) -> None: @@ -457,12 +525,28 @@ def test_decode_non_strict_should_work(expr: Any, type: Any, expected: str) -> N ("expr", "type", "expected"), [ ('{"a":1, "b":2}', SimpleItem, SimpleItem(1, 2)), - ('{"a":1, "b":2}', SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields(1, 2)), - ('{"a":1}', SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields(1)), - ("{}", SimpleItemWithOnlyOptionalFields, SimpleItemWithOnlyOptionalFields()), + ( + '{"a":1, "b":2}', + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(1, 2), + ), + ( + '{"a":1}', + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(1), + ), + ( + "{}", + SimpleItemWithOnlyOptionalFields, + SimpleItemWithOnlyOptionalFields(), + ), ("{}", SimpleItemWithNoFields, SimpleItemWithNoFields()), ("{}", (SimpleItemWithNoFields, SimpleItem), SimpleItemWithNoFields()), - ('{"a":1, "b":2}', (SimpleItemWithNoFields, SimpleItem), SimpleItem(1, 2)), + ( + '{"a":1, "b":2}', + (SimpleItemWithNoFields, SimpleItem), + SimpleItem(1, 2), + ), ], ) def test_decode_strict_should_work(expr: Any, type: Any, expected: str) -> None: @@ -984,14 +1068,20 @@ def test_really_complex_data() -> None: ), text_document=TextDocumentClientCapabilities( synchronization=TextDocumentSyncClientCapabilities( - dynamic_registration=True, will_save=True, will_save_wait_until=True, did_save=True + dynamic_registration=True, + will_save=True, + will_save_wait_until=True, + did_save=True, ), completion=CompletionClientCapabilities( dynamic_registration=True, completion_item=CompletionClientCapabilitiesCompletionItemType( snippet_support=True, commit_characters_support=True, - documentation_format=[MarkupKind.MARKDOWN, MarkupKind.PLAIN_TEXT], + documentation_format=[ + MarkupKind.MARKDOWN, + MarkupKind.PLAIN_TEXT, + ], deprecated_support=True, preselect_support=True, tag_support=CompletionClientCapabilitiesCompletionItemTypeTagSupportType( @@ -999,10 +1089,17 @@ def test_really_complex_data() -> None: ), insert_replace_support=True, resolve_support=CompletionClientCapabilitiesCompletionItemTypeResolveSupportType( - properties=["documentation", "detail", "additionalTextEdits"] + properties=[ + "documentation", + "detail", + "additionalTextEdits", + ] ), insert_text_mode_support=CompletionClientCapabilitiesCompletionItemTypeInsertTextModeSupportType( - value_set=[InsertTextMode.AS_IS, InsertTextMode.ADJUST_INDENTATION] + value_set=[ + InsertTextMode.AS_IS, + InsertTextMode.ADJUST_INDENTATION, + ] ), ), completion_item_kind=CompletionClientCapabilitiesCompletionItemKindType( @@ -1037,12 +1134,16 @@ def test_really_complex_data() -> None: context_support=True, ), hover=HoverClientCapabilities( - dynamic_registration=True, content_format=[MarkupKind.MARKDOWN, MarkupKind.PLAIN_TEXT] + dynamic_registration=True, + content_format=[MarkupKind.MARKDOWN, MarkupKind.PLAIN_TEXT], ), signature_help=SignatureHelpClientCapabilities( dynamic_registration=True, signature_information=SignatureHelpClientCapabilitiesSignatureInformationType( - documentation_format=[MarkupKind.MARKDOWN, MarkupKind.PLAIN_TEXT], + documentation_format=[ + MarkupKind.MARKDOWN, + MarkupKind.PLAIN_TEXT, + ], parameter_information=SignatureHelpClientCapabilitiesSignatureInformationTypeParameterInformationType( label_offset_support=True ), @@ -1129,21 +1230,27 @@ def test_really_complex_data() -> None: publish_diagnostics=PublishDiagnosticsClientCapabilities( related_information=True, tag_support=PublishDiagnosticsClientCapabilitiesTagSupportType( - value_set=[DiagnosticTag.UNNECESSARY, DiagnosticTag.DEPRECATED] + value_set=[ + DiagnosticTag.UNNECESSARY, + DiagnosticTag.DEPRECATED, + ] ), version_support=False, code_description_support=True, data_support=True, ), folding_range=FoldingRangeClientCapabilities( - dynamic_registration=True, range_limit=5000, line_folding_only=True + dynamic_registration=True, + range_limit=5000, + line_folding_only=True, ), selection_range=SelectionRangeClientCapabilities(dynamic_registration=True), linked_editing_range=LinkedEditingRangeClientCapabilities(dynamic_registration=True), call_hierarchy=CallHierarchyClientCapabilities(dynamic_registration=True), semantic_tokens=SemanticTokensClientCapabilities( requests=SemanticTokensClientCapabilitiesRequestsType( - range=True, full=SemanticTokensClientCapabilitiesRequestsTypeFullType1(delta=True) + range=True, + full=SemanticTokensClientCapabilitiesRequestsTypeFullType1(delta=True), ), token_types=[ "namespace", diff --git a/tests/robotcode/core/test_safeeval.py b/tests/robotcode/core/test_safeeval.py index 9c14f5189..6c4e7752c 100644 --- a/tests/robotcode/core/test_safeeval.py +++ b/tests/robotcode/core/test_safeeval.py @@ -45,10 +45,7 @@ def test_safe_eval_should_not_allow_builtin_names(expression: str) -> None: @pytest.mark.parametrize( ("expression", "result"), - [ - ("'PATH' in environ", True), - (r"bool(re.match('\\d+', '1234'))", True), - ], + [("'PATH' in environ", True), (r"bool(re.match('\\d+', '1234'))", True)], ) def test_safe_eval_simple_should_support_custom_globals(expression: str, result: Any) -> None: assert safe_eval(expression, {"environ": dict(os.environ), "re": re}) == result diff --git a/tests/robotcode/jsonrpc/test_jsonrpcprotocol.py b/tests/robotcode/jsonrpc/test_jsonrpcprotocol.py index 1c9877a63..1645ac98f 100644 --- a/tests/robotcode/jsonrpc/test_jsonrpcprotocol.py +++ b/tests/robotcode/jsonrpc/test_jsonrpcprotocol.py @@ -97,7 +97,7 @@ async def test_receive_invalid_jsonmessage_should_throw_send_an_error() -> None: @pytest.mark.asyncio() -async def test_receive_a_request_with_invalid_protocol_version_should_send_an_error() -> None: +async def test_receive_a_request_with_invalid_protocol_version_should_send_an_error() -> (None): protocol = DummyJsonRPCProtocol(None) message = JsonRPCRequest(id=1, method="doSomething", params={}) @@ -115,7 +115,11 @@ async def test_receive_a_request_with_invalid_protocol_version_should_send_an_er async def test_receive_an_error_should_work() -> None: protocol = DummyJsonRPCProtocol(None) - message = JsonRPCError(id=1, result=None, error=JsonRPCErrorObject(code=1, message="test", data="this is the data")) + message = JsonRPCError( + id=1, + result=None, + error=JsonRPCErrorObject(code=1, message="test", data="this is the data"), + ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") @@ -130,7 +134,10 @@ async def test_receive_response_should_work() -> None: r = protocol.send_request("dummy/method", ["dummy", "data"], list) - message = JsonRPCResponse(id=cast(JsonRPCRequest, protocol.sended_message).id, result=["dummy", "data"]) + message = JsonRPCResponse( + id=cast(JsonRPCRequest, protocol.sended_message).id, + result=["dummy", "data"], + ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") data = header + json_message @@ -156,13 +163,14 @@ async def test_receive_invalid_id_in_response_should_send_an_error() -> None: @pytest.mark.asyncio() -async def test_send_request_receive_response_should_work_without_param_type_work() -> None: +async def test_send_request_receive_response_should_work_without_param_type_work() -> (None): protocol = DummyJsonRPCProtocol(None) r: Any = protocol.send_request("dummy/method", ["dummy", "data"]) message = JsonRPCResponse( - id=cast(JsonRPCRequest, protocol.sended_message).id, result=MessageActionItem(title="hi there") + id=cast(JsonRPCRequest, protocol.sended_message).id, + result=MessageActionItem(title="hi there"), ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") @@ -182,7 +190,8 @@ async def test_receive_response_should_work_with_dataclass() -> None: r = protocol.send_request("dummy/method", ["dummy", "data"], MessageActionItem) message = JsonRPCResponse( - id=cast(JsonRPCRequest, protocol.sended_message).id, result=MessageActionItem(title="hi there") + id=cast(JsonRPCRequest, protocol.sended_message).id, + result=MessageActionItem(title="hi there"), ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") @@ -201,7 +210,8 @@ async def test_receive_response_should_work_with_generic_list() -> None: r = protocol.send_request("dummy/method", ["dummy", "data"], List[MessageActionItem]) message = JsonRPCResponse( - id=cast(JsonRPCRequest, protocol.sended_message).id, result=[MessageActionItem(title="hi there")] + id=cast(JsonRPCRequest, protocol.sended_message).id, + result=[MessageActionItem(title="hi there")], ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") @@ -214,13 +224,14 @@ async def test_receive_response_should_work_with_generic_list() -> None: @pytest.mark.asyncio() -async def test_receive_response_with_generic_dict_should_return_unchanged() -> None: +async def test_receive_response_with_generic_dict_should_return_unchanged() -> (None): protocol = DummyJsonRPCProtocol(None) r = protocol.send_request("dummy/method", ["dummy", "data"], List[Dict[str, Any]]) message = JsonRPCResponse( - id=cast(JsonRPCRequest, protocol.sended_message).id, result=[MessageActionItem(title="hi there")] + id=cast(JsonRPCRequest, protocol.sended_message).id, + result=[MessageActionItem(title="hi there")], ) json_message = as_json(message).encode("utf-8") header = f"Content-Length: {len(json_message)}\r\n\r\n".encode("ascii") diff --git a/tests/robotcode/language_server/common/test_text_document.py b/tests/robotcode/language_server/common/test_text_document.py index 3cac784c4..58fced971 100644 --- a/tests/robotcode/language_server/common/test_text_document.py +++ b/tests/robotcode/language_server/common/test_text_document.py @@ -9,7 +9,12 @@ def test_apply_full_change_should_work() -> None: text = """first""" new_text = """changed""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_full_change(1, new_text) @@ -20,11 +25,21 @@ def test_apply_full_change_should_work() -> None: def test_apply_apply_incremental_change_at_begining_should_work() -> None: text = """first""" new_text = """changed""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_incremental_change( - 1, Range(start=Position(line=0, character=0), end=Position(line=0, character=0)), new_text + 1, + Range( + start=Position(line=0, character=0), + end=Position(line=0, character=0), + ), + new_text, ) assert document.text() == new_text + text @@ -34,11 +49,21 @@ def test_apply_apply_incremental_change_at_end_should_work() -> None: text = """first""" new_text = """changed""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_incremental_change( - 1, Range(start=Position(line=0, character=len(text)), end=Position(line=0, character=len(text))), new_text + 1, + Range( + start=Position(line=0, character=len(text)), + end=Position(line=0, character=len(text)), + ), + new_text, ) assert document.text() == text + new_text @@ -48,13 +73,23 @@ def test_save_and_revert_should_work() -> None: text = """first""" new_text = """changed""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text assert not document.revert(None) document.apply_incremental_change( - 2, Range(start=Position(line=0, character=len(text)), end=Position(line=0, character=len(text))), new_text + 2, + Range( + start=Position(line=0, character=len(text)), + end=Position(line=0, character=len(text)), + ), + new_text, ) assert document.text() == text + new_text @@ -67,7 +102,12 @@ def test_save_and_revert_should_work() -> None: assert document.version == 1 document.apply_incremental_change( - 2, Range(start=Position(line=0, character=len(text)), end=Position(line=0, character=len(text))), new_text + 2, + Range( + start=Position(line=0, character=len(text)), + end=Position(line=0, character=len(text)), + ), + new_text, ) document.save(None, None) @@ -87,11 +127,21 @@ def test_apply_apply_incremental_change_in_the_middle_should_work() -> None: second changed line third""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_incremental_change( - 1, Range(start=Position(line=1, character=7), end=Position(line=1, character=7)), new_text + 1, + Range( + start=Position(line=1, character=7), + end=Position(line=1, character=7), + ), + new_text, ) assert document.text() == expected @@ -104,11 +154,21 @@ def test_apply_apply_incremental_change_with_start_line_eq_len_lines_should_work third""" new_text = """changed """ - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_incremental_change( - 1, Range(start=Position(line=3, character=7), end=Position(line=3, character=8)), new_text + 1, + Range( + start=Position(line=3, character=7), + end=Position(line=3, character=8), + ), + new_text, ) assert document.text() == text + new_text @@ -118,19 +178,34 @@ def test_apply_apply_incremental_change_with_wrong_range_should_raise_invalidran text = """first""" new_text = """changed""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text with pytest.raises(InvalidRangeError): document.apply_incremental_change( - 1, Range(start=Position(line=4, character=len(text)), end=Position(line=0, character=len(text))), new_text + 1, + Range( + start=Position(line=4, character=len(text)), + end=Position(line=0, character=len(text)), + ), + new_text, ) def test_apply_none_change_should_work() -> None: text = """first""" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_none_change() @@ -145,7 +220,12 @@ def test_lines_should_give_the_lines_of_the_document() -> None: third """ - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.text() == text document.apply_none_change() @@ -166,7 +246,12 @@ class WeakReferencable: key = WeakReferencable() data = "some data" - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) document.set_data(key, data) assert document.get_data(key) == data @@ -190,7 +275,12 @@ def test_document_get_set_cache_with_function_should_work() -> None: def get_data(document: TextDocument, data: str) -> str: return prefix + data - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) assert document.get_cache(get_data, "data") == "1data" @@ -215,7 +305,12 @@ def test_document_get_set_cache_with_method_should_work() -> None: second third """ - document = TextDocument(document_uri="file:///test.robot", language_id="robotframework", version=1, text=text) + document = TextDocument( + document_uri="file:///test.robot", + language_id="robotframework", + version=1, + text=text, + ) prefix = "1" diff --git a/tests/robotcode/language_server/robotframework/parts/conftest.py b/tests/robotcode/language_server/robotframework/parts/conftest.py index de3b32029..21e225737 100644 --- a/tests/robotcode/language_server/robotframework/parts/conftest.py +++ b/tests/robotcode/language_server/robotframework/parts/conftest.py @@ -19,13 +19,19 @@ from robotcode.core.utils.dataclasses import as_dict from robotcode.language_server.common.parts.diagnostics import DiagnosticsMode from robotcode.language_server.common.text_document import TextDocument -from robotcode.language_server.robotframework.configuration import AnalysisConfig, RobotCodeConfig, RobotConfig +from robotcode.language_server.robotframework.configuration import ( + AnalysisConfig, + RobotCodeConfig, + RobotConfig, +) from robotcode.language_server.robotframework.protocol import ( RobotLanguageServerProtocol, ) from robotcode.language_server.robotframework.server import RobotLanguageServer -from tests.robotcode.language_server.robotframework.tools import generate_test_id +from tests.robotcode.language_server.robotframework.tools import ( + generate_test_id, +) from .pytest_regtestex import RegTestFixtureEx @@ -38,7 +44,9 @@ def event_loop() -> Iterator[asyncio.AbstractEventLoop]: @pytest_asyncio.fixture(scope="session", ids=generate_test_id) -async def protocol(request: pytest.FixtureRequest) -> AsyncIterator[RobotLanguageServerProtocol]: +async def protocol( + request: pytest.FixtureRequest, +) -> AsyncIterator[RobotLanguageServerProtocol]: root_path = Path(Path(__file__).absolute().parent, "data") robotcode_cache_path = root_path / ".robotcode_cache" @@ -54,9 +62,7 @@ async def protocol(request: pytest.FixtureRequest) -> AsyncIterator[RobotLanguag ) ) - initialization_options = { - "python_path": ["./lib", "./resources"], - } + initialization_options = {"python_path": ["./lib", "./resources"]} protocol = RobotLanguageServerProtocol(server) protocol._initialize( @@ -77,9 +83,7 @@ async def protocol(request: pytest.FixtureRequest) -> AsyncIterator[RobotLanguag robot=RobotConfig( python_path=["./lib", "./resources"], env={"ENV_VAR": "1"}, - variables={ - "CMD_VAR": "1", - }, + variables={"CMD_VAR": "1"}, ), analysis=AnalysisConfig(diagnostic_mode=DiagnosticsMode.OFF), ), @@ -97,12 +101,17 @@ async def protocol(request: pytest.FixtureRequest) -> AsyncIterator[RobotLanguag @pytest.fixture(scope="module") -async def test_document(request: pytest.FixtureRequest) -> AsyncIterator[TextDocument]: +async def test_document( + request: pytest.FixtureRequest, +) -> AsyncIterator[TextDocument]: data_path = Path(request.param) data = data_path.read_text("utf-8") document = TextDocument( - document_uri=data_path.absolute().as_uri(), language_id="robotframework", version=1, text=data + document_uri=data_path.absolute().as_uri(), + language_id="robotframework", + version=1, + text=data, ) try: yield document @@ -112,6 +121,4 @@ async def test_document(request: pytest.FixtureRequest) -> AsyncIterator[TextDoc @pytest.fixture() def regtest(request: pytest.FixtureRequest) -> RegTestFixtureEx: - item = request.node - - return RegTestFixtureEx(request, item.nodeid) + return RegTestFixtureEx(request) 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 f87b171fd..1b976d493 100644 --- a/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json +++ b/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json @@ -29,7 +29,7 @@ //"robotcode.robot.paths": ["./tests", "./tests1"] "robotcode.analysis.referencesCodeLens": true, "robotcode.analysis.diagnosticMode": "workspace", - "robotcode.analysis.progressMode": "detailed", + "robotcode.analysis.progressMode": "simple", "robotcode.analysis.findUnusedReferences": true, "robotcode.analysis.cache.saveLocation": "workspaceFolder", "robotcode.analysis.cache.ignoredLibraries": [ diff --git a/tests/robotcode/language_server/robotframework/parts/data/lib/Signatures.py b/tests/robotcode/language_server/robotframework/parts/data/lib/Signatures.py index 7e10436e8..40490f2bc 100644 --- a/tests/robotcode/language_server/robotframework/parts/data/lib/Signatures.py +++ b/tests/robotcode/language_server/robotframework/parts/data/lib/Signatures.py @@ -4,21 +4,33 @@ class TestEnum(Enum): """This is a test enum.""" + A = 1 B = 2 C = 3 + class Signatures: """This class is used to test the signature generation.""" - def __init__(self, l: Optional[List[int]] = None, s: str = "Hello World!", i: int = 0, b: bool = False) -> None: + def __init__( + self, + l: Optional[List[int]] = None, + s: str = "Hello World!", + i: int = 0, + b: bool = False, + ) -> None: """This is the constructor of the class.""" self.l = l - def do_something(self, i: int, b: bool, l: Optional[List[int]] = None) -> Tuple[Any, ...]: + def do_something( + self, i: int, b: bool, l: Optional[List[int]] = None + ) -> Tuple[Any, ...]: """This is a method of the class.""" return (self.l, i, b) - def do_something_with_enum(self, i: int, b: bool, l: TestEnum = TestEnum.A) -> Tuple[Any, ...]: + def do_something_with_enum( + self, i: int, b: bool, l: TestEnum = TestEnum.A + ) -> Tuple[Any, ...]: """This is a method of the class.""" return (self.l, i, b) diff --git a/tests/robotcode/language_server/robotframework/parts/data/lib/dynamicvars.py b/tests/robotcode/language_server/robotframework/parts/data/lib/dynamicvars.py index b038ffae6..38a41ef46 100644 --- a/tests/robotcode/language_server/robotframework/parts/data/lib/dynamicvars.py +++ b/tests/robotcode/language_server/robotframework/parts/data/lib/dynamicvars.py @@ -2,6 +2,4 @@ def get_variables(p: str) -> Dict[str, str]: - return { - p + "_A_VAR_FROM_LIB": "1", - } + return {p + "_A_VAR_FROM_LIB": "1"} diff --git a/tests/robotcode/language_server/robotframework/parts/pytest_regtestex.py b/tests/robotcode/language_server/robotframework/parts/pytest_regtestex.py index 751b3bdb3..b5a01705c 100644 --- a/tests/robotcode/language_server/robotframework/parts/pytest_regtestex.py +++ b/tests/robotcode/language_server/robotframework/parts/pytest_regtestex.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path from robotcode.robot.utils import get_robot_version @@ -9,9 +9,19 @@ class RegTestFixtureEx(RegTestFixture): @property - def old_result_file(self) -> str: - return os.path.join(self.test_folder, "_regtest_outputs", f"{rf_version}", self.old_output_file_name) + def old_result_file(self) -> Path: + return Path( + self.test_folder, + "_regtest_outputs", + rf_version, + self.old_output_file_name, + ) @property - def result_file(self) -> str: - return os.path.join(self.test_folder, "_regtest_outputs", f"{rf_version}", self.output_file_name) + def result_file(self) -> Path: + return Path( + self.test_folder, + "_regtest_outputs", + rf_version, + self.output_file_name, + ) diff --git a/tests/robotcode/language_server/robotframework/parts/test_code_action_show_documentation.py b/tests/robotcode/language_server/robotframework/parts/test_code_action_show_documentation.py index 990fcce3f..60ff351d7 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_code_action_show_documentation.py +++ b/tests/robotcode/language_server/robotframework/parts/test_code_action_show_documentation.py @@ -28,7 +28,12 @@ @pytest.mark.parametrize( ("test_document", "data"), - generate_tests_from_source_document(Path(Path(__file__).parent, "data/tests/code_action_show_documentation.robot")), + generate_tests_from_source_document( + Path( + Path(__file__).parent, + "data/tests/code_action_show_documentation.robot", + ) + ), indirect=["test_document"], ids=generate_test_id, scope="module", @@ -47,9 +52,14 @@ def split(action: Union[Command, CodeAction]) -> Union[Command, CodeAction]: result = protocol.robot_code_action_documentation.collect( protocol.robot_code_action_documentation, test_document, - Range(Position(line=data.line, character=data.character), Position(line=data.line, character=data.character)), + Range( + Position(line=data.line, character=data.character), + Position(line=data.line, character=data.character), + ), CodeActionContext( - diagnostics=[], only=[CodeActionKind.SOURCE.value], trigger_kind=CodeActionTriggerKind.INVOKED + diagnostics=[], + only=[CodeActionKind.SOURCE.value], + trigger_kind=CodeActionTriggerKind.INVOKED, ), ) regtest.write( @@ -57,7 +67,11 @@ def split(action: Union[Command, CodeAction]) -> Union[Command, CodeAction]: { "data": data, "result": sorted( - (split(v) for v in result), key=lambda v: (v.title, v.kind if isinstance(v, CodeAction) else None) + (split(v) for v in result), + key=lambda v: ( + v.title, + v.kind if isinstance(v, CodeAction) else None, + ), ) if result else result, diff --git a/tests/robotcode/language_server/robotframework/parts/test_document_highlight.py b/tests/robotcode/language_server/robotframework/parts/test_document_highlight.py index e7a0e5a71..4aae210f3 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_document_highlight.py +++ b/tests/robotcode/language_server/robotframework/parts/test_document_highlight.py @@ -31,7 +31,9 @@ def test( data: GeneratedTestData, ) -> None: result = protocol.robot_document_highlight.collect( - protocol.robot_document_highlight, test_document, Position(line=data.line, character=data.character) + protocol.robot_document_highlight, + test_document, + Position(line=data.line, character=data.character), ) regtest.write( yaml.dump( diff --git a/tests/robotcode/language_server/robotframework/parts/test_document_symbols.py b/tests/robotcode/language_server/robotframework/parts/test_document_symbols.py index a996bb397..f4f8427d7 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_document_symbols.py +++ b/tests/robotcode/language_server/robotframework/parts/test_document_symbols.py @@ -21,7 +21,8 @@ def split( - values: Optional[Union[List[DocumentSymbol], List[SymbolInformation]]], data: GeneratedTestData + values: Optional[Union[List[DocumentSymbol], List[SymbolInformation]]], + data: GeneratedTestData, ) -> Iterator[Optional[Union[DocumentSymbol, SymbolInformation]]]: p = Position(data.line, data.character) @@ -36,7 +37,11 @@ def split( elif isinstance(value, SymbolInformation): if p in value.location.range: yield dataclasses.replace( - value, location=dataclasses.replace(value.location, uri=Uri(value.location.uri).to_path().name) + value, + location=dataclasses.replace( + value.location, + uri=Uri(value.location.uri).to_path().name, + ), ) @@ -53,16 +58,18 @@ def test( test_document: TextDocument, data: GeneratedTestData, ) -> None: - result = protocol.robot_document_symbols.collect( - protocol.hover, - test_document, - ) + result = protocol.robot_document_symbols.collect(protocol.hover, test_document) regtest.write( yaml.dump( { "data": data, - "result": next(reversed([l for l in split(result, data) if l is not None]), None) if result else result, + "result": next( + reversed([l for l in split(result, data) if l is not None]), + None, + ) + if result + else result, } ) ) diff --git a/tests/robotcode/language_server/robotframework/parts/test_foldingrange.py b/tests/robotcode/language_server/robotframework/parts/test_foldingrange.py index 196cfd76d..2fce60cbe 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_foldingrange.py +++ b/tests/robotcode/language_server/robotframework/parts/test_foldingrange.py @@ -23,7 +23,8 @@ def prepend_protocol_data( - protocol: Iterable[Any], data: Iterable[Union[Tuple[Any, Path, GeneratedTestData], Any]] + protocol: Iterable[Any], + data: Iterable[Union[Tuple[Any, Path, GeneratedTestData], Any]], ) -> Iterator[Union[Tuple[Any, Path, GeneratedTestData], Any]]: for p in protocol: for d in data: @@ -47,12 +48,12 @@ def generate_foldingrange_test_id(params: Any) -> Any: [ ClientCapabilities( text_document=TextDocumentClientCapabilities( - folding_range=FoldingRangeClientCapabilities(line_folding_only=True), + folding_range=FoldingRangeClientCapabilities(line_folding_only=True) ) ), ClientCapabilities( text_document=TextDocumentClientCapabilities( - folding_range=FoldingRangeClientCapabilities(line_folding_only=False), + folding_range=FoldingRangeClientCapabilities(line_folding_only=False) ) ), ], diff --git a/tests/robotcode/language_server/robotframework/parts/test_goto_definition.py b/tests/robotcode/language_server/robotframework/parts/test_goto_definition.py index 5dd523588..d8efd1366 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_goto_definition.py +++ b/tests/robotcode/language_server/robotframework/parts/test_goto_definition.py @@ -32,7 +32,9 @@ def test_definition( data: GeneratedTestData, ) -> None: result = protocol.robot_goto.collect_definition( - protocol.robot_goto, test_document, Position(line=data.line, character=data.character) + protocol.robot_goto, + test_document, + Position(line=data.line, character=data.character), ) regtest.write(yaml.dump({"data": data, "result": split(result)})) diff --git a/tests/robotcode/language_server/robotframework/parts/test_goto_implementation.py b/tests/robotcode/language_server/robotframework/parts/test_goto_implementation.py index 4f8357383..89ed3e16c 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_goto_implementation.py +++ b/tests/robotcode/language_server/robotframework/parts/test_goto_implementation.py @@ -20,7 +20,7 @@ def split( - result: Union[Location, LocationLink, List[Location], List[LocationLink], None] + result: Union[Location, LocationLink, List[Location], List[LocationLink], None], ) -> Union[Location, LocationLink, List[Location], List[LocationLink], None]: if result is None: return None diff --git a/tests/robotcode/language_server/robotframework/parts/test_references.py b/tests/robotcode/language_server/robotframework/parts/test_references.py index fef51ecf2..ffe7ead0d 100644 --- a/tests/robotcode/language_server/robotframework/parts/test_references.py +++ b/tests/robotcode/language_server/robotframework/parts/test_references.py @@ -2,11 +2,7 @@ import pytest import yaml -from robotcode.core.lsp.types import ( - Location, - Position, - ReferenceContext, -) +from robotcode.core.lsp.types import Location, Position, ReferenceContext from robotcode.language_server.common.text_document import TextDocument from robotcode.language_server.robotframework.protocol import ( RobotLanguageServerProtocol, @@ -50,7 +46,10 @@ def split(location: Location) -> Location: yaml.dump( { "data": data, - "result": sorted((split(v) for v in result), key=lambda v: (v.uri, v.range.start, v.range.end)) + "result": sorted( + (split(v) for v in result), + key=lambda v: (v.uri, v.range.start, v.range.end), + ) if result else result, } diff --git a/tests/robotcode/language_server/robotframework/tools.py b/tests/robotcode/language_server/robotframework/tools.py index 6a34c39de..9c76b5b50 100644 --- a/tests/robotcode/language_server/robotframework/tools.py +++ b/tests/robotcode/language_server/robotframework/tools.py @@ -41,9 +41,19 @@ def generate_tests_from_source_document( else: yield path, GeneratedTestData(name, current_line, start) if end - start > 2: - yield path, GeneratedTestData(name, current_line, int(start + (end - start) / 2)) - - yield path, GeneratedTestData(name, current_line, end - 1) + yield ( + path, + GeneratedTestData( + name, + current_line, + int(start + (end - start) / 2), + ), + ) + + yield ( + path, + GeneratedTestData(name, current_line, end - 1), + ) else: current_line = line diff --git a/tests/robotcode/robot/config/test_loader.py b/tests/robotcode/robot/config/test_loader.py index 16e09a0be..0c88ba63a 100644 --- a/tests/robotcode/robot/config/test_loader.py +++ b/tests/robotcode/robot/config/test_loader.py @@ -3,7 +3,11 @@ from typing import Iterator, Tuple import pytest -from robotcode.robot.config.loader import DiscoverdBy, find_project_root, get_config_files_from_folder +from robotcode.robot.config.loader import ( + DiscoverdBy, + find_project_root, + get_config_files_from_folder, +) @pytest.fixture() @@ -18,7 +22,10 @@ def temp_project(tmp_path: Path) -> Iterator[Path]: @pytest.mark.parametrize( ("path_str", "discovered_by"), - {("pyproject.toml", DiscoverdBy.PYPROJECT_TOML), ("robot.toml", DiscoverdBy.ROBOT_TOML)}, + { + ("pyproject.toml", DiscoverdBy.PYPROJECT_TOML), + ("robot.toml", DiscoverdBy.ROBOT_TOML), + }, ) def test_find_project_root_from_root_from_toml_works( temp_project: Path, path_str: str, discovered_by: DiscoverdBy @@ -31,7 +38,10 @@ def test_find_project_root_from_root_from_toml_works( assert discovery_by == discovered_by -@pytest.mark.parametrize(("path_str", "discovered_by"), {(".git", DiscoverdBy.GIT), (".hg", DiscoverdBy.HG)}) +@pytest.mark.parametrize( + ("path_str", "discovered_by"), + {(".git", DiscoverdBy.GIT), (".hg", DiscoverdBy.HG)}, +) def test_find_project_root_from_root_from_vcs_dirs_works( temp_project: Path, path_str: str, discovered_by: DiscoverdBy ) -> None: @@ -55,7 +65,9 @@ def test_find_project_from_sub_dir_robot_toml_works(temp_project: Path) -> None: assert discovery_by == DiscoverdBy.ROBOT_TOML -def test_find_project_from_sub_dir_file_robot_toml_works(temp_project: Path) -> None: +def test_find_project_from_sub_dir_file_robot_toml_works( + temp_project: Path, +) -> None: root_robot_toml = temp_project / "robot.toml" root_robot_toml.touch() @@ -69,7 +81,9 @@ def test_find_project_from_sub_dir_file_robot_toml_works(temp_project: Path) -> assert discovery_by == DiscoverdBy.ROBOT_TOML -def test_find_project_from_sub_dir_file_robot_toml_no_root_found(temp_project: Path) -> None: +def test_find_project_from_sub_dir_file_robot_toml_no_root_found( + temp_project: Path, +) -> None: sub_dir = temp_project / "subdir" sub_dir.mkdir() file = sub_dir / "file.robot" @@ -80,7 +94,9 @@ def test_find_project_from_sub_dir_file_robot_toml_no_root_found(temp_project: P assert discovery_by == DiscoverdBy.NOT_FOUND -def test_find_project_from_several_sub_dirs_and_files_robot_toml_works(temp_project: Path) -> None: +def test_find_project_from_several_sub_dirs_and_files_robot_toml_works( + temp_project: Path, +) -> None: root_robot_toml = temp_project / "robot.toml" root_robot_toml.touch() diff --git a/tests/robotcode/robot/config/test_model.py b/tests/robotcode/robot/config/test_model.py index 5af5570a1..f4c8a365e 100644 --- a/tests/robotcode/robot/config/test_model.py +++ b/tests/robotcode/robot/config/test_model.py @@ -1,7 +1,11 @@ from typing import Any, Dict import pytest -from robotcode.core.utils.dataclasses import TypeValidationError, as_dict, from_dict +from robotcode.core.utils.dataclasses import ( + TypeValidationError, + as_dict, + from_dict, +) from robotcode.robot.config.model import RobotConfig @@ -15,7 +19,9 @@ ({"args": 1, "output_dir": 1}), ], ) -def test_robot_config_cannot_assign_invalid_args(kwargs: Dict[str, Any]) -> None: +def test_robot_config_cannot_assign_invalid_args( + kwargs: Dict[str, Any], +) -> None: with pytest.raises(TypeValidationError): RobotConfig(**kwargs) diff --git a/tests/robotcode/robot/config/test_profile.py b/tests/robotcode/robot/config/test_profile.py index 7c620444d..e7286664a 100644 --- a/tests/robotcode/robot/config/test_profile.py +++ b/tests/robotcode/robot/config/test_profile.py @@ -110,7 +110,10 @@ def test_if_profile_is_not_defined_an_error_is_raised() -> None: """ config = load_robot_config_from_robot_toml_str(data) - with pytest.raises(ValueError, match="Can't find any profiles matching the pattern 'nonexistent'."): + with pytest.raises( + ValueError, + match="Can't find any profiles matching the pattern 'nonexistent'.", + ): config.combine_profiles("nonexistent") @@ -274,7 +277,12 @@ def test_profiles_tag_stat_combine_generates_correct() -> None: """ config = load_robot_config_from_robot_toml_str(data) cmd_line = config.combine_profiles().evaluated().build_command_line() - assert cmd_line == ["--tagstatcombine", "tag1:tag2", "--tagstatcombine", "tag3:tag4"] + assert cmd_line == [ + "--tagstatcombine", + "tag1:tag2", + "--tagstatcombine", + "tag3:tag4", + ] def test_profiles_flatten_keywords_supports_literals_and_patterns() -> None: