Implement rp CLI: rich-print markdown, JSON, code, and more

- Add typer CLI with auto file-type detection by extension
- Support markdown (rich.Markdown), JSON (rich.JSON), and 30+ code
  languages via Pygments syntax highlighting
- Features: line numbers toggle, pager support, stdin input,
  configurable color themes
- Project metadata: MIT license, author, classifiers, entry point
This commit is contained in:
2026-04-01 15:56:57 -07:00
parent a1b5e72032
commit 1abfcebc24
7 changed files with 404 additions and 4 deletions

119
src/rp/cli.py Normal file
View File

@@ -0,0 +1,119 @@
"""CLI entry point for rp — rich print files beautifully."""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Annotated, Optional
import typer
from rich.console import Console
from rp import __version__
from rp.detect import detect_type
from rp.render import RenderOptions, render
app = typer.Typer(
name="rp",
help="Rich print files — markdown, JSON, code, and more — beautifully in your terminal.",
no_args_is_help=False,
rich_markup_mode="rich",
)
_THEMES = [
"monokai",
"github-dark",
"one-dark",
"vim",
"vs",
"nord",
"dracula",
"solarized-dark",
"solarized-light",
"default",
"friendly",
"native",
"emacs",
]
def version_callback(value: bool) -> None:
if value:
console = Console()
console.print(f"rp {__version__}")
raise typer.Exit()
@app.command()
def main(
file: Annotated[
Optional[Path],
typer.Argument(help="File to rich-print. Use '-' or omit to read from stdin."),
] = None,
type: Annotated[
Optional[str],
typer.Option(
"--type", "-t", help="Force file type (e.g. json, python, markdown)."
),
] = None,
theme: Annotated[
str,
typer.Option("--theme", help="Pygments color theme."),
] = "monokai",
line_numbers: Annotated[
Optional[bool],
typer.Option(
"--line-numbers/--no-line-numbers",
help="Show line numbers. Default: auto.",
),
] = None,
pager: Annotated[
Optional[bool],
typer.Option(
"--pager/--no-pager",
help="Use pager for output. Default: auto-detect.",
),
] = None,
version: Annotated[
Optional[bool],
typer.Option(
"--version",
"-v",
help="Show version.",
callback=version_callback,
is_eager=True,
),
] = None,
) -> None:
"""Rich print files beautifully in your terminal."""
console = Console()
if file is None or str(file) == "-":
content = sys.stdin.read()
file_path = None
else:
if not file.exists():
console.print(f"[red]Error:[/red] File not found: {file}")
raise typer.Exit(code=1)
if file.is_dir():
console.print(f"[red]Error:[/red] Is a directory: {file}")
raise typer.Exit(code=1)
content = file.read_text(encoding="utf-8")
file_path = str(file)
if not content:
console.print("[dim]Empty input, nothing to display.[/dim]")
raise typer.Exit()
file_type = detect_type(path=file_path, content=content, explicit_type=type)
options = RenderOptions(
theme=theme,
line_numbers=line_numbers,
pager=pager,
)
render(content, file_type, console, options)
if __name__ == "__main__":
app()