From 3d8a22dbfeb801a76a259f351ddd62593b235e76 Mon Sep 17 00:00:00 2001 From: Daniel Biehl Date: Sun, 15 Oct 2023 21:13:42 +0200 Subject: [PATCH] perf(langserver): speedup Visitor and AsyncVisitor a little bit --- .vscode/launch.json | 3 ++- .../robotframework/diagnostics/analyzer.py | 2 ++ .../parts/code_action_helper_mixin.py | 1 + .../robotframework/utils/ast_utils.py | 1 + .../robotframework/utils/async_ast.py | 19 +++++++++++++++---- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index fdc4bf097..c079720c0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -44,7 +44,8 @@ // "--no-pager", //"config", "info", "list", // "analyze", - "profiles", "list" + // "profiles", "list" + "discover", "tests", "--tags" // "." ] }, 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 5393a193b..88c453a09 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 @@ -75,6 +75,8 @@ def __init__( ) -> None: from robot.parsing.model.statements import Template, TestTemplate + super().__init__() + self.model = model self.namespace = namespace self.finder = finder 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 ed32bdaee..49c75062f 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 @@ -29,6 +29,7 @@ class CodeActionDataBase: class FindSectionsVisitor(Visitor): def __init__(self) -> None: + super().__init__() self.keyword_sections: List[ast.AST] = [] self.variable_sections: List[ast.AST] = [] self.setting_sections: List[ast.AST] = [] diff --git a/packages/language_server/src/robotcode/language_server/robotframework/utils/ast_utils.py b/packages/language_server/src/robotcode/language_server/robotframework/utils/ast_utils.py index 92a597b92..9eaff9c7a 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/utils/ast_utils.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/utils/ast_utils.py @@ -120,6 +120,7 @@ def range_from_token(token: Token) -> Range: class FirstAndLastRealStatementFinder(async_ast.Visitor): def __init__(self) -> None: + super().__init__() self.first_statement: Optional[ast.AST] = None self.last_statement: Optional[ast.AST] = None diff --git a/packages/language_server/src/robotcode/language_server/robotframework/utils/async_ast.py b/packages/language_server/src/robotcode/language_server/robotframework/utils/async_ast.py index 10573c9a6..c36882ebd 100644 --- a/packages/language_server/src/robotcode/language_server/robotframework/utils/async_ast.py +++ b/packages/language_server/src/robotcode/language_server/robotframework/utils/async_ast.py @@ -1,5 +1,5 @@ import ast -from typing import Any, AsyncIterator, Callable, Iterator, Optional, Type, cast +from typing import Any, AsyncIterator, Callable, Dict, Iterator, Optional, Type __all__ = ["iter_fields", "iter_child_nodes", "AsyncVisitor"] @@ -47,20 +47,31 @@ async def iter_nodes(node: ast.AST) -> AsyncIterator[ast.AST]: class VisitorFinder: - def _find_visitor(self, cls: Type[Any]) -> Optional[Callable[..., Any]]: + __NOT_SET = object() + + def __init__(self) -> None: + self.__cache: Dict[Type[Any], Optional[Callable[..., Any]]] = {} + + def __find_visitor(self, cls: Type[Any]) -> Optional[Callable[..., Any]]: if cls is ast.AST: return None method_name = "visit_" + cls.__name__ if hasattr(self, method_name): method = getattr(self, method_name) if callable(method): - return cast("Callable[..., Any]", method) + return method # type: ignore for base in cls.__bases__: method = self._find_visitor(base) if method: - return cast("Callable[..., Any]", method) + return method # type: ignore return None + def _find_visitor(self, cls: Type[Any]) -> Optional[Callable[..., Any]]: + r = self.__cache.get(cls, self.__NOT_SET) + if r is self.__NOT_SET: + self.__cache[cls] = r = self.__find_visitor(cls) + return r # type: ignore + class AsyncVisitor(VisitorFinder): async def visit(self, node: ast.AST) -> None: