""" Source code feed — reads engine/*.py and emits non-blank, non-comment lines as scroll items. Used by --code mode. Depends on: nothing (stdlib only). """ import ast from pathlib import Path _ENGINE_DIR = Path(__file__).resolve().parent def _scope_map(source: str) -> dict[int, str]: """Return {line_number: scope_label} for every line in source. Nodes are sorted by range size descending so inner scopes overwrite outer ones, guaranteeing the narrowest enclosing scope wins. """ try: tree = ast.parse(source) except SyntaxError: return {} nodes = [] for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): end = getattr(node, "end_lineno", node.lineno) span = end - node.lineno nodes.append((span, node)) # Largest range first → inner scopes overwrite on second pass nodes.sort(key=lambda x: x[0], reverse=True) scope = {} for _, node in nodes: end = getattr(node, "end_lineno", node.lineno) if isinstance(node, ast.ClassDef): label = node.name else: label = f"{node.name}()" for ln in range(node.lineno, end + 1): scope[ln] = label return scope def fetch_code(): """Read engine/*.py and return (items, line_count, 0). Each item is (text, src, ts) where: text = the code line (rstripped, indentation preserved) src = enclosing function/class name, e.g. 'stream()' or '' ts = dotted module path, e.g. 'engine.scroll' """ items = [] for path in sorted(_ENGINE_DIR.glob("*.py")): module = f"engine.{path.stem}" source = path.read_text(encoding="utf-8") scope = _scope_map(source) for lineno, raw in enumerate(source.splitlines(), start=1): stripped = raw.strip() if not stripped or stripped.startswith("#"): continue label = scope.get(lineno, "") items.append((raw.rstrip(), label, module)) return items, len(items), 0