""" ctstruct/renderers/emoji_markup.py CTTree → 이모지 마크업 텍스트 렌더러 """ from __future__ import annotations from ..node import CTNode from ..tree import CTTree from ..types import NodeType from .base import BaseRenderer class EmojiMarkupRenderer(BaseRenderer): """ CTTree 를 이모지 마크업 텍스트로 렌더링. EmojiMarkupParser 와 같은 구분자를 사용하면 완전한 왕복 변환(roundtrip)이 보장됩니다. Args: title_mark: 문서 제목 앞에 붙는 마커 (기본 "✅") section_marks: 섹션 레벨별 마커 목록 (기본 ["🔹", "🔸"]) 인덱스 0 → level 1, 인덱스 1 → level 2, … names_per_line: 한 줄에 출력할 최대 이름 수 (기본 0 = 제한 없음) """ def __init__( self, title_mark: str = "✅", section_marks: list[str] | None = None, names_per_line: int = 0, ) -> None: self.title_mark = title_mark self.section_marks = section_marks if section_marks is not None else ["🔹", "🔸"] self.names_per_line = names_per_line def render(self, tree: CTTree) -> str: lines: list[str] = [] title = tree.root.attr("title") if title: lines.append(f"{self.title_mark}{title}") lines.append("") for child in tree.root.children: lines.extend(self._render_node(child)) return "\n".join(lines).rstrip() + "\n" # ── 내부 ───────────────────────────────────────────────── def _render_node(self, node: CTNode) -> list[str]: if node.node_type == NodeType.SECTION: return self._render_section(node) if node.node_type == NodeType.LIST: return self._render_list(node) return [] def _render_section(self, node: CTNode) -> list[str]: level = node.attr("level", 1) mark = self.section_marks[level - 1] if level <= len(self.section_marks) else "" out = [f"{mark}{node.content or ''}"] for child in node.children: out.extend(self._render_node(child)) return out def _render_list(self, node: CTNode) -> list[str]: names = [item.content or "" for item in node.children if item.node_type == NodeType.LIST_ITEM] category = node.attr("category") prefix = f"({category}) " if category else "" if self.names_per_line > 0: chunks = [names[i:i + self.names_per_line] for i in range(0, len(names), self.names_per_line)] lines = [prefix + " ".join(chunk) for chunk in chunks] # prefix는 첫 줄에만 if len(lines) > 1: lines = [lines[0]] + [" ".join(chunk) for chunk in chunks[1:]] else: lines = [prefix + " ".join(names)] if names else [] return lines + [""] # 목록 뒤 빈 줄