Something went wrong. Try again.
A CLI to (readonly) query Scrivener project in textual form
scrivener cli
Something went wrong. Try again.
4.7 kB · 145 lines
Python
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146"""Plain-text rendering of the binder tree and document views."""
from __future__ import annotations
from scriv.binder import UNTITLED, BinderItem, walkfrom scriv.project import ScrivenerProject
TYPE_TAG = { "DraftFolder": "(draft)", "ResearchFolder": "(research)", "TrashFolder": "(trash)",}
def display_title(item: BinderItem) -> str: return item.title or UNTITLED
def badges(project: ScrivenerProject, item: BinderItem) -> str: parts: list[str] = [] label = project.metadata.label_name(item.label_id) if label and item.label_id not in (None, "-1"): parts.append(label) status = project.metadata.status_name(item.status_id) if status and item.status_id not in (None, "-1"): parts.append(status) kws = [project.metadata.keyword_name(k) for k in item.keyword_ids] kws = [k for k in kws if k] if kws: parts.append("{" + ",".join(kws) + "}") if not item.include_in_compile: parts.append("no-compile") return " ".join(f"[{p}]" for p in parts)
def render_tree( project: ScrivenerProject, *, ids: bool = False, show_badges: bool = True, root: BinderItem | None = None,) -> str: root = root or project.root lines: list[str] = [] _render_node(project, root, "", True, True, lines, ids, show_badges) return "\n".join(lines)
def _render_node( project: ScrivenerProject, node: BinderItem, prefix: str, is_last: bool, is_root: bool, lines: list[str], ids: bool, show_badges: bool,) -> None: if is_root and node.type == "Root": # Synthetic root: render children only, but with no connector. title = display_title(node) lines.append(title) children = node.children for i, child in enumerate(children): _render_node( project, child, "", i == len(children) - 1, False, lines, ids, show_badges, ) return connector = "" if is_root else ("└── " if is_last else "├── ") title = display_title(node) tag = TYPE_TAG.get(node.type, "") label = f"{tag} {title}" if tag else title suffix = "" if show_badges: b = badges(project, node) if b: suffix = f" {b}" if ids: suffix = f" <id {node.id}>{suffix}" lines.append(f"{prefix}{connector}{label}{suffix}") children = node.children child_prefix = prefix + ("" if is_root else (" " if is_last else "│ ")) for i, child in enumerate(children): _render_node( project, child, child_prefix, i == len(children) - 1, False, lines, ids, show_badges, )
def render_flat( project: ScrivenerProject, *, ids: bool = False, show_badges: bool = True, text_only: bool = False,) -> str: lines: list[str] = [] for node in walk(project.root): if node.type == "Root": continue if text_only and not node.is_text: continue path = node.slash_path() label = path or display_title(node) suffix = "" if show_badges: b = badges(project, node) if b: suffix = f" {b}" if ids: suffix = f" <id {node.id}>{suffix}" lines.append(f"{label}{suffix}") return "\n".join(lines)
def render_meta(project: ScrivenerProject, item: BinderItem) -> str: md = project.metadata rows: list[tuple[str, str]] = [ ("Title", display_title(item)), ("Type", item.type), ("ID", item.id), ("Path", item.slash_path() or "/"), ("Created", item.created or "-"), ("Modified", item.modified or "-"), ("Label", md.label_name(item.label_id) or "-"), ("Status", md.status_name(item.status_id) or "-"), ("Keywords", ", ".join(filter(None, (md.keyword_name(k) for k in item.keyword_ids))) or "-"), ("Include in compile", "yes" if item.include_in_compile else "no"), ("Icon", item.icon or "-"), ] if item.target_type: target = f"{item.target_type}" if item.target_count is not None: target += f", {item.target_count}" rows.append(("Target", target)) if item.custom_metadata: for field, value in item.custom_metadata.items(): rows.append((f"custom:{field}", value or "-")) if item.is_text: rows.append(("Words", str(project.word_count(item)))) rows.append(("Synopsis", project.synopsis(item) or "-")) width = max(len(k) for k, _ in rows) return "\n".join(f"{k.ljust(width)} {v}" for k, v in rows)