Skip to content

Commit 39e509e

Browse files
committed
fix(langserver): cache to_markdown methods
1 parent 39f368f commit 39e509e

File tree

2 files changed

+60
-14
lines changed

2 files changed

+60
-14
lines changed

packages/robot/src/robotcode/robot/diagnostics/entities.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
TYPE_CHECKING,
66
Any,
77
Callable,
8+
Generic,
89
List,
910
Optional,
1011
Tuple,
1112
TypeVar,
1213
cast,
1314
)
1415

16+
from typing_extensions import Concatenate, ParamSpec
17+
1518
from robot.parsing.lexer.tokens import Token
1619
from robotcode.core.lsp.types import Position, Range
1720
from robotcode.robot.utils.match import normalize
@@ -41,6 +44,39 @@ def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
4144
return cast(_F, wrapper)
4245

4346

47+
P = ParamSpec("P")
48+
R = TypeVar("R")
49+
50+
51+
class cached_method(Generic[P, R]): # noqa: N801
52+
def __init__(
53+
self, func: Optional[Callable[Concatenate[Any, P], R]] = None, *, maxsize: Optional[int] = None
54+
) -> None:
55+
self.func: Optional[Callable[Concatenate[Any, P], R]] = func
56+
self._maxsize = maxsize
57+
self.cache_name: Optional[str] = None
58+
if func is not None:
59+
functools.update_wrapper(self, func)
60+
61+
def __set_name__(self, owner: type, name: str) -> None:
62+
self.cache_name = f"__cached_{owner.__name__}_{name}"
63+
64+
def __call__(self, func: Callable[Concatenate[Any, P], R]) -> "cached_method[P, R]":
65+
self.func = func
66+
functools.update_wrapper(self, func)
67+
return self
68+
69+
def __get__(self, instance: Any, owner: Optional[type] = None) -> Callable[P, R]:
70+
cached = instance.__dict__.get(self.cache_name, _NOT_SET)
71+
if cached is _NOT_SET:
72+
assert self.func is not None
73+
74+
bound_method = self.func.__get__(instance, owner)
75+
cached = functools.lru_cache(maxsize=self._maxsize)(bound_method)
76+
instance.__dict__[self.cache_name] = cached
77+
return cast(Callable[P, R], cached)
78+
79+
4480
@dataclass
4581
class SourceEntity:
4682
line_no: int

packages/robot/src/robotcode/robot/diagnostics/library_doc.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
LibraryArgumentDefinition,
8181
NativeValue,
8282
SourceEntity,
83+
cached_method,
8384
single_call,
8485
)
8586

@@ -362,7 +363,8 @@ def __hash__(self) -> int:
362363
)
363364
)
364365

365-
def to_markdown(self, header_level: int = 2) -> str:
366+
@cached_method
367+
def to_markdown(self, header_level: int = 2, only_doc: bool = False) -> str:
366368
result = ""
367369

368370
result += f"##{'#' * header_level} {self.name} ({self.type})\n\n"
@@ -378,19 +380,22 @@ def to_markdown(self, header_level: int = 2) -> str:
378380
else:
379381
result += self.doc
380382

381-
if self.members:
382-
result += f"\n\n###{'#' * header_level} Allowed Values:\n\n"
383-
result += "- " + "\n- ".join(f"`{m.name}`" for m in self.members)
384-
385-
if self.items:
386-
result += f"\n\n###{'#' * header_level} Dictionary Structure:\n\n"
387-
result += "```\n{"
388-
result += "\n ".join(f"'{m.key}': <{m.type}> {'# optional' if not m.required else ''}" for m in self.items)
389-
result += "\n}\n```"
383+
if not only_doc:
384+
if self.members:
385+
result += f"\n\n###{'#' * header_level} Allowed Values:\n\n"
386+
result += "- " + "\n- ".join(f"`{m.name}`" for m in self.members)
387+
388+
if self.items:
389+
result += f"\n\n###{'#' * header_level} Dictionary Structure:\n\n"
390+
result += "```\n{"
391+
result += "\n ".join(
392+
f"'{m.key}': <{m.type}> {'# optional' if not m.required else ''}" for m in self.items
393+
)
394+
result += "\n}\n```"
390395

391-
if self.accepts:
392-
result += f"\n\n###{'#' * header_level} Converted Types:\n\n"
393-
result += "- " + "\n- ".join(self.accepts)
396+
if self.accepts:
397+
result += f"\n\n###{'#' * header_level} Converted Types:\n\n"
398+
result += "- " + "\n- ".join(self.accepts)
394399

395400
return result
396401

@@ -475,6 +480,7 @@ def from_robot(arg: Any) -> ArgumentInfo:
475480
def __str__(self) -> str:
476481
return self.signature()
477482

483+
@cached_method
478484
def signature(self, add_types: bool = True) -> str:
479485
prefix = ""
480486
if self.kind == KeywordArgumentKind.POSITIONAL_ONLY_MARKER:
@@ -721,6 +727,7 @@ def range(self) -> Range:
721727
),
722728
)
723729

730+
@cached_method
724731
def to_markdown(
725732
self,
726733
add_signature: bool = True,
@@ -796,7 +803,7 @@ def escape_pipe(s: str) -> str:
796803

797804
result += (
798805
f"\n| `{prefix}{a.name!s}`"
799-
f'| {": " if a.types else " "}'
806+
f"| {': ' if a.types else ' '}"
800807
f"{escaped_pipe.join(f'`{escape_pipe(s)}`' for s in a.types) if a.types else ''} "
801808
f"| {'=' if a.default_value is not None else ''} "
802809
f"| {f'`{a.default_value!s}`' if a.default_value else ''} |"
@@ -832,6 +839,7 @@ def signature(self) -> str:
832839
+ ")"
833840
)
834841

842+
@cached_method
835843
def parameter_signature(self, full_signatures: Optional[Sequence[int]] = None) -> str:
836844
return (
837845
"("
@@ -1094,6 +1102,7 @@ def range(self) -> Range:
10941102
),
10951103
)
10961104

1105+
@cached_method
10971106
def to_markdown(
10981107
self,
10991108
add_signature: bool = True,
@@ -1250,6 +1259,7 @@ class VariablesDoc(LibraryDoc):
12501259

12511260
variables: List[ImportedVariableDefinition] = field(default_factory=list)
12521261

1262+
@cached_method
12531263
def to_markdown(
12541264
self,
12551265
add_signature: bool = True,

0 commit comments

Comments
 (0)