|
33 | 33 | from collections import deque |
34 | 34 | from pathlib import Path |
35 | 35 | from subprocess import CompletedProcess |
36 | | -from typing import Dict, List, Set, Tuple, Union |
| 36 | +from typing import Dict, Iterator, List, Set, Tuple, Union |
37 | 37 |
|
38 | 38 | import networkx as nx |
39 | 39 |
|
|
42 | 42 | from cldk.models.typescript import ( |
43 | 43 | TSApplication, |
44 | 44 | TSCallable, |
| 45 | + TSCallableOverview, |
45 | 46 | TSCallsite, |
46 | 47 | TSClass, |
47 | 48 | TSClassAttribute, |
@@ -554,3 +555,57 @@ def get_classes_with_decorators(self, decorators: List[str]) -> Dict[str, List[s |
554 | 555 | if dec.name in wanted: |
555 | 556 | result[dec.name].append(sig) |
556 | 557 | return result |
| 558 | + |
| 559 | + # -----[ bulk / projected accessors ]----- |
| 560 | + def _iter_callables(self) -> Iterator[Tuple[TSCallable, str | None, str | None]]: |
| 561 | + """Yield ``(callable, owner_signature, owner_kind)`` for every callable in the |
| 562 | + application, including inner/nested callables. The owner map is built only from |
| 563 | + ``_methods_by_class`` keyed against ``_classes``/``_interfaces``: namespace-owned |
| 564 | + functions and module-level/nested callables are never in that map, so they correctly come |
| 565 | + out owner-less (None, None), per the closed "class"|"interface" owner_kind set.""" |
| 566 | + owner_of: Dict[str, Tuple[str, str]] = {} |
| 567 | + for owner_sig, methods in self._methods_by_class.items(): |
| 568 | + if owner_sig in self._classes: |
| 569 | + owner_kind = "class" |
| 570 | + elif owner_sig in self._interfaces: |
| 571 | + owner_kind = "interface" |
| 572 | + else: |
| 573 | + continue |
| 574 | + for m in methods.values(): |
| 575 | + owner_of[m.signature] = (owner_sig, owner_kind) |
| 576 | + for sig, c in self._callables.items(): |
| 577 | + owner_sig, owner_kind = owner_of.get(sig, (None, None)) |
| 578 | + yield c, owner_sig, owner_kind |
| 579 | + |
| 580 | + def get_callables_overview(self) -> List[TSCallableOverview]: |
| 581 | + """Return a lightweight overview of every callable in the application (see |
| 582 | + :meth:`TSAnalysisBackend.get_callables_overview`).""" |
| 583 | + return [TSCallableOverview.from_callable(c, owner_sig, owner_kind) for c, owner_sig, owner_kind in self._iter_callables()] |
| 584 | + |
| 585 | + def get_method_bodies(self, signatures: List[str]) -> Dict[str, str]: |
| 586 | + """Return ``{signature: code}`` for the requested signatures that exist and have a body |
| 587 | + (omits callables whose ``code`` is ``None``, e.g. implicit constructors).""" |
| 588 | + result: Dict[str, str] = {} |
| 589 | + for sig in signatures: |
| 590 | + c = self._callables.get(sig) |
| 591 | + if c is not None and c.code is not None: |
| 592 | + result[sig] = c.code |
| 593 | + return result |
| 594 | + |
| 595 | + def get_decorated_callables(self, markers: List[str]) -> List[TSCallableOverview]: |
| 596 | + """Return overviews of callables decorated with any of ``markers``.""" |
| 597 | + marker_set = set(markers) |
| 598 | + return [ |
| 599 | + TSCallableOverview.from_callable(c, owner_sig, owner_kind) |
| 600 | + for c, owner_sig, owner_kind in self._iter_callables() |
| 601 | + if marker_set.intersection(d.name for d in c.decorators) |
| 602 | + ] |
| 603 | + |
| 604 | + def get_callsites_for(self, signatures: List[str]) -> Dict[str, List[TSCallsite]]: |
| 605 | + """Return ``{signature: call_sites}`` for the requested signatures that exist.""" |
| 606 | + result: Dict[str, List[TSCallsite]] = {} |
| 607 | + for sig in signatures: |
| 608 | + c = self._callables.get(sig) |
| 609 | + if c is not None: |
| 610 | + result[sig] = list(c.call_sites) |
| 611 | + return result |
0 commit comments