From 87cb416ae2daf390ef49ca0e8016c691faa3ecfe Mon Sep 17 00:00:00 2001 From: Daniel Biehl Date: Mon, 1 Jan 2024 17:43:53 +0100 Subject: [PATCH] refactor(langserver): remove async code from code actions --- .../common/parts/code_action.py | 36 +++++++-------- .../parts/code_action_documentation.py | 29 +++--------- .../parts/code_action_helper_mixin.py | 2 +- .../parts/code_action_quick_fixes.py | 44 ++++++++----------- .../parts/code_action_refactor.py | 26 +++++------ .../parts/data/.vscode/settings.json | 14 +++--- .../test_code_action_show_documentation.py | 22 +++------- 7 files changed, 69 insertions(+), 104 deletions(-) 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 b8cb1eb84..740935556 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 @@ -1,10 +1,8 @@ -from __future__ import annotations - -from asyncio import CancelledError +from concurrent.futures import CancelledError from itertools import chain from typing import TYPE_CHECKING, Any, Final, List, Optional, Union, cast -from robotcode.core.async_tools import async_tasking_event +from robotcode.core.event import event from robotcode.core.lsp.types import ( CodeAction, CodeActionContext, @@ -16,7 +14,7 @@ TextDocumentIdentifier, ) from robotcode.core.utils.logging import LoggingDescriptor -from robotcode.core.utils.threading import threaded +from robotcode.core.utils.threading import check_thread_canceled, threaded 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 @@ -29,17 +27,17 @@ class CodeActionProtocolPart(LanguageServerProtocolPart): _logger: Final = LoggingDescriptor() - def __init__(self, parent: LanguageServerProtocol) -> None: + def __init__(self, parent: "LanguageServerProtocol") -> None: super().__init__(parent) - @async_tasking_event - async def collect( + @event + def collect( sender, document: TextDocument, range: Range, context: CodeActionContext # NOSONAR ) -> Optional[List[Union[Command, CodeAction]]]: ... - @async_tasking_event - async def resolve(sender, code_action: CodeAction) -> Optional[CodeAction]: # NOSONAR + @event + def resolve(sender, code_action: CodeAction) -> Optional[CodeAction]: # NOSONAR ... def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @@ -61,7 +59,7 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None: @rpc_method(name="textDocument/codeAction", param_type=CodeActionParams) @threaded - async def _text_document_code_action( + def _text_document_code_action( self, text_document: TextDocumentIdentifier, range: Range, @@ -81,13 +79,11 @@ async def _text_document_code_action( for r in c.related_information: r.location.range = document.range_from_utf16(r.location.range) - for result in await self.collect( - self, - document, - document.range_from_utf16(range), - context, - callback_filter=language_id_filter(document), + for result in self.collect( + self, document, document.range_from_utf16(range), context, callback_filter=language_id_filter(document) ): + check_thread_canceled() + if isinstance(result, BaseException): if not isinstance(result, CancelledError): self._logger.exception(result, exc_info=result) @@ -102,7 +98,7 @@ async def _text_document_code_action( @rpc_method(name="codeAction/resolve", param_type=CodeAction) @threaded - async def _text_document_code_action_resolve( + def _text_document_code_action_resolve( self, params: CodeAction, *args: Any, @@ -110,7 +106,9 @@ async def _text_document_code_action_resolve( ) -> CodeAction: results: List[CodeAction] = [] - for result in await self.resolve(self, params): + for result in self.resolve(self, params): + check_thread_canceled() + if isinstance(result, BaseException): if not isinstance(result, CancelledError): self._logger.exception(result, exc_info=result) 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 a5b38975e..08c4ca7d5 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 @@ -243,7 +243,7 @@ def _ensure_http_server_started(self) -> None: ] ) @_logger.call - async def collect( + def collect( self, sender: Any, document: TextDocument, @@ -270,11 +270,8 @@ async def collect( if CodeActionKind.SOURCE.value in context.only and range in range_from_token( node.get_token(RobotToken.NAME) ): - url = await self.build_url( - node.name, - node.args if isinstance(node, LibraryImport) else (), - document, - namespace, + url = self.build_url( + node.name, node.args if isinstance(node, LibraryImport) else (), document, namespace ) return [self.open_documentation_code_action(url)] @@ -338,26 +335,14 @@ async def collect( if entry is None: return None - url = await 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 = await 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)] @@ -374,7 +359,7 @@ def open_documentation_code_action(self, url: str) -> CodeAction: ), ) - async def build_url( + def build_url( self, name: str, args: Tuple[Any, ...], @@ -423,7 +408,7 @@ async def build_url( @rpc_method(name="robot/documentationServer/convertUri", param_type=ConvertUriParams) @threaded - async def _convert_uri(self, uri: str, *args: Any, **kwargs: Any) -> Optional[str]: + def _convert_uri(self, uri: str, *args: Any, **kwargs: Any) -> Optional[str]: real_uri = Uri(uri) folder = self.parent.workspace.get_workspace_folder(real_uri) 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 9f7cd435f..c942f37b5 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 @@ -62,7 +62,7 @@ def find_keyword_sections(node: ast.AST) -> Optional[List[ast.AST]]: class CodeActionHelperMixin: - async def create_insert_keyword_workspace_edit( + def create_insert_keyword_workspace_edit( self, document: TextDocument, model: ast.AST, namespace: Namespace, insert_text: str ) -> Tuple[str, Range]: keyword_sections = find_keyword_sections(model) 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 0db17125c..fc1da266f 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,5 +1,3 @@ -from __future__ import annotations - from collections import defaultdict from dataclasses import dataclass from string import Template as StringTemplate @@ -91,7 +89,7 @@ class CodeActionData(CodeActionDataBase): class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin, CodeActionHelperMixin): _logger = LoggingDescriptor() - def __init__(self, parent: RobotLanguageServerProtocol) -> None: + def __init__(self, parent: "RobotLanguageServerProtocol") -> None: super().__init__(parent) parent.code_action.collect.add(self.collect) @@ -101,12 +99,12 @@ def __init__(self, parent: RobotLanguageServerProtocol) -> None: @language_id("robotframework") @code_action_kinds([CodeActionKind.QUICK_FIX]) - async def collect( + def collect( 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_")): - code_actions = await method(document, range, context) + code_actions = method(document, range, context) if code_actions: result.extend(code_actions) @@ -115,17 +113,17 @@ async def collect( return None - async def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: + def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: if code_action.data is not None and isinstance(code_action.data, Mapping): 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}")) - await method(code_action, data=from_dict(code_action.data, CodeActionData)) + method(code_action, data=from_dict(code_action.data, CodeActionData)) return None - async def code_action_create_keyword( + def code_action_create_keyword( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: result: List[Union[Command, CodeAction]] = [] @@ -184,9 +182,7 @@ async def code_action_create_keyword( return result if result else None - async def resolve_code_action_create_keyword( - self, code_action: CodeAction, data: CodeActionData - ) -> Optional[CodeAction]: + def resolve_code_action_create_keyword(self, code_action: CodeAction, data: CodeActionData) -> Optional[CodeAction]: document = self.parent.documents.get(data.document_uri) if document is None: return None @@ -240,7 +236,7 @@ async def resolve_code_action_create_keyword( else: dest_document = document - code_action.edit, select_range = await self._apply_create_keyword(dest_document, insert_text) + code_action.edit, select_range = self._apply_create_keyword(dest_document, insert_text) code_action.command = Command( SHOW_DOCUMENT_SELECT_AND_RENAME_COMMAND, @@ -251,13 +247,11 @@ async def resolve_code_action_create_keyword( return None - async def _apply_create_keyword(self, document: TextDocument, insert_text: str) -> Tuple[WorkspaceEdit, Range]: + def _apply_create_keyword(self, document: TextDocument, insert_text: str) -> Tuple[WorkspaceEdit, Range]: model = self.parent.documents_cache.get_model(document, False) namespace = self.parent.documents_cache.get_namespace(document) - insert_text, insert_range = await self.create_insert_keyword_workspace_edit( - document, model, namespace, insert_text - ) + insert_text, insert_range = self.create_insert_keyword_workspace_edit(document, model, namespace, insert_text) we = WorkspaceEdit( document_changes=[ @@ -276,7 +270,7 @@ async def _apply_create_keyword(self, document: TextDocument, insert_text: str) return we, selection_range - async def code_action_disable_robotcode_diagnostics_for_line( + def code_action_disable_robotcode_diagnostics_for_line( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: if ( @@ -313,7 +307,7 @@ async def code_action_disable_robotcode_diagnostics_for_line( return None - async def resolve_code_action_disable_robotcode_diagnostics_for_line( + def resolve_code_action_disable_robotcode_diagnostics_for_line( self, code_action: CodeAction, data: CodeActionData ) -> Optional[CodeAction]: if data.range.start.line == data.range.end.line and data.range.start.character <= data.range.end.character: @@ -350,7 +344,7 @@ async def resolve_code_action_disable_robotcode_diagnostics_for_line( return None - async def code_action_create_local_variable( + def code_action_create_local_variable( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: result: List[Union[Command, CodeAction]] = [] @@ -405,7 +399,7 @@ async def code_action_create_local_variable( return result if result else None - async def resolve_code_action_create_local_variable( + def resolve_code_action_create_local_variable( self, code_action: CodeAction, data: CodeActionData ) -> Optional[CodeAction]: if data.range.start.line == data.range.end.line and data.range.start.character <= data.range.end.character: @@ -457,7 +451,7 @@ async def resolve_code_action_create_local_variable( return None - async def code_action_create_suite_variable( + def code_action_create_suite_variable( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: result: List[Union[Command, CodeAction]] = [] @@ -499,7 +493,7 @@ async def code_action_create_suite_variable( return result if result else None - async def resolve_code_action_create_suite_variable( + def resolve_code_action_create_suite_variable( self, code_action: CodeAction, data: CodeActionData ) -> Optional[CodeAction]: if data.range.start.line == data.range.end.line and data.range.start.character <= data.range.end.character: @@ -589,7 +583,7 @@ async def resolve_code_action_create_suite_variable( return code_action return None - async def code_action_add_argument( + def code_action_add_argument( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: result: List[Union[Command, CodeAction]] = [] @@ -638,9 +632,7 @@ async def code_action_add_argument( return result if result else None - async def resolve_code_action_add_argument( - self, code_action: CodeAction, data: CodeActionData - ) -> Optional[CodeAction]: + def resolve_code_action_add_argument(self, code_action: CodeAction, data: CodeActionData) -> Optional[CodeAction]: if data.range.start.line == data.range.end.line and data.range.start.character <= data.range.end.character: document = self.parent.documents.get(data.document_uri) if document is None: 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 a2e260b53..a04ef9d5f 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 @@ -1,5 +1,3 @@ -from __future__ import annotations - import ast import itertools from dataclasses import dataclass @@ -97,7 +95,7 @@ class CodeActionData(CodeActionDataBase): class RobotCodeActionRefactorProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin, CodeActionHelperMixin): _logger = LoggingDescriptor() - def __init__(self, parent: RobotLanguageServerProtocol) -> None: + def __init__(self, parent: "RobotLanguageServerProtocol") -> None: super().__init__(parent) parent.code_action.collect.add(self.collect) @@ -107,12 +105,12 @@ def __init__(self, parent: RobotLanguageServerProtocol) -> None: @language_id("robotframework") @code_action_kinds([CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION, CODE_ACTION_KIND_SURROUND_WITH]) - async def collect( + def collect( 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_")): - code_actions = await method(document, range, context) + code_actions = method(document, range, context) if code_actions: result.extend(code_actions) @@ -121,13 +119,13 @@ async def collect( return None - async def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: + def resolve(self, sender: Any, code_action: CodeAction) -> Optional[CodeAction]: if code_action.data is not None and isinstance(code_action.data, Mapping): 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}")) - await method(code_action, data=from_dict(code_action.data, CodeActionData)) + method(code_action, data=from_dict(code_action.data, CodeActionData)) return None @@ -257,7 +255,7 @@ def get_valid_nodes_in_range(self, model: ast.AST, range: Range, also_return: bo return result - async def code_action_surround( + def code_action_surround( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: from robot.parsing.model.blocks import Keyword, TestCase @@ -321,7 +319,7 @@ async def code_action_surround( ), ] - async def resolve_code_action_surround(self, code_action: CodeAction, data: CodeActionData) -> Optional[CodeAction]: + def resolve_code_action_surround(self, code_action: CodeAction, data: CodeActionData) -> Optional[CodeAction]: insert_range = data.range if not insert_range: @@ -392,7 +390,7 @@ async def resolve_code_action_surround(self, code_action: CodeAction, data: Code return code_action - async def code_action_assign_result_to_variable( + def code_action_assign_result_to_variable( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: from robot.parsing.lexer import Token as RobotToken @@ -436,7 +434,7 @@ async def code_action_assign_result_to_variable( return None - async def resolve_code_action_assign_result_to_variable( + def resolve_code_action_assign_result_to_variable( self, code_action: CodeAction, data: CodeActionData ) -> Optional[CodeAction]: from robot.parsing.lexer import Token as RobotToken @@ -507,7 +505,7 @@ async def resolve_code_action_assign_result_to_variable( return code_action - async def code_action_extract_keyword( + def code_action_extract_keyword( self, document: TextDocument, range: Range, context: CodeActionContext ) -> Optional[List[Union[Command, CodeAction]]]: from robot.parsing.model.blocks import Keyword, TestCase @@ -545,7 +543,7 @@ async def code_action_extract_keyword( ), ] - async def resolve_code_action_extract_keyword( + def resolve_code_action_extract_keyword( self, code_action: CodeAction, data: CodeActionData ) -> Optional[CodeAction]: from robot.parsing.model.blocks import Keyword, TestCase @@ -642,7 +640,7 @@ async def resolve_code_action_extract_keyword( if assigned_variables: keyword_text += "\n RETURN " + " ".join(n.name for n in assigned_variables.keys()) - keyword_text, keyword_range = await self.create_insert_keyword_workspace_edit( + keyword_text, keyword_range = self.create_insert_keyword_workspace_edit( document, model, namespace, keyword_text ) 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 8cc0dac14..0b2cd98bc 100644 --- a/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json +++ b/tests/robotcode/language_server/robotframework/parts/data/.vscode/settings.json @@ -14,7 +14,7 @@ }, "robotcode.languageServer.extraArgs": [ // "--debugpy", - //"--debugpy-wait-for-client", + // "--debugpy-wait-for-client", // "--log", ], "robotcode.robocop.enabled": false, @@ -34,12 +34,12 @@ "**/lib/alibrary.py", "LibraryWithErrors" ], - "robotcode.extraArgs": [ - "--log", - "--log-level", - "DEBUG", - // "--log-calls" - ], + // "robotcode.extraArgs": [ + // "--log", + // "--log-level", + // "DEBUG", + // // "--log-calls" + // ], "robotcode.robot.variableFiles": [ "${EXECDIR}/resources/testvars.yml" ], 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 6d88cd121..990fcce3f 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 @@ -1,4 +1,3 @@ -import asyncio from pathlib import Path from typing import Union @@ -34,9 +33,7 @@ ids=generate_test_id, scope="module", ) -@pytest.mark.usefixtures("protocol") -@pytest.mark.asyncio() -async def test( +def test( regtest: RegTestFixtureEx, protocol: RobotLanguageServerProtocol, test_document: TextDocument, @@ -47,18 +44,13 @@ def split(action: Union[Command, CodeAction]) -> Union[Command, CodeAction]: action.command.arguments = [""] return action - result = await asyncio.wait_for( - 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) - ), - CodeActionContext( - diagnostics=[], only=[CodeActionKind.SOURCE.value], trigger_kind=CodeActionTriggerKind.INVOKED - ), + 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)), + CodeActionContext( + diagnostics=[], only=[CodeActionKind.SOURCE.value], trigger_kind=CodeActionTriggerKind.INVOKED ), - 60, ) regtest.write( yaml.dump(