Skip to main content

rich/
protocol.rs

1//! Rendering & extension protocols.
2//!
3//! Port of upstream `rich/protocol.py` + `rich/abc.py` + the highlighter
4//! interface. **These traits are the sanctioned extension points of the port.**
5//! Extensions in `rich-ext` (and, later, third-party plugins) implement them;
6//! the faithful core only ever ships upstream's built-in implementations. See
7//! docs/PLUGINS.md.
8
9use crate::console::{Console, ConsoleOptions};
10use crate::measure::Measurement;
11use crate::segment::Segment;
12use crate::text::Text;
13
14/// Anything that can be rendered to a stream of [`Segment`]s within a width.
15///
16/// The Rust equivalent of upstream's `__rich_console__(console, options)`
17/// protocol. Implement it to make a custom type printable by [`Console`]. The
18/// `options` carry the available width (and, later, height/justify) the
19/// renderable must fit into. Newlines between lines are emitted as ordinary
20/// segments containing `\n`.
21pub trait Renderable {
22    fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment>;
23
24    /// The `(minimum, maximum)` cell width this renderable wants. The default
25    /// assumes the renderable fills the available width (e.g. `Panel`, `Table`);
26    /// `Text` overrides it with its content width so the top-level print path can
27    /// shrink to fit. Port of `__rich_measure__` / `Measurement.get`.
28    fn measure(&self, _console: &Console, options: &ConsoleOptions) -> Measurement {
29        Measurement::new(options.max_width, options.max_width)
30    }
31
32    /// Whether a top-level `Console::print` shrinks this renderable to its
33    /// measured width. Upstream renders every top-level renderable at the full
34    /// console width, so the default is `false`; an extension may opt in.
35    fn fit_to_measurement(&self) -> bool {
36        false
37    }
38
39    /// The `Text` a top-level print renders in place of this renderable.
40    ///
41    /// Upstream's `Console._collect_renderables` rebuilds printed `str`/`Text`
42    /// values through `Text(sep, end=end).join(...)`, whose `blank_copy` takes
43    /// `justify`, `overflow` and `no_wrap` from the separator. Only `Text`
44    /// overrides this.
45    #[doc(hidden)]
46    fn printed_text(&self) -> Option<Text> {
47        None
48    }
49}
50
51/// Optional line-streaming extension point for renderables.
52///
53/// Mirrors the incremental consumption of upstream's rendering generators.
54/// Consumers can write each visual line immediately instead of collecting the
55/// complete segment stream. Implementations may still retain source data for
56/// measurement. This trait keeps streaming hooks out of inherent core APIs.
57pub trait LineRenderable: Renderable {
58    /// Emit styled visual lines without trailing newlines, stopping immediately
59    /// on the callback's first error. An empty segment represents a blank line;
60    /// calling the callback zero times represents no output.
61    fn try_for_each_line<E>(
62        &self,
63        console: &Console,
64        options: &ConsoleOptions,
65        emit: impl FnMut(Vec<Segment>) -> Result<(), E>,
66    ) -> Result<(), E>;
67}
68
69/// Transfer already-owned table rows without cloning every cell string.
70///
71/// This extension point changes ownership only. Column definitions, measurement
72/// and rendering follow the table's existing rules, including missing/extra cells.
73/// Producers that parse into owned strings can release their row collection as
74/// they populate a table instead of retaining a second complete copy.
75pub trait OwnedTableRows {
76    fn extend_owned_rows(&mut self, rows: Vec<Vec<String>>) -> &mut Self;
77}
78
79/// A transformer that adds style spans to [`Text`] (e.g. syntax/number/URL
80/// highlighting). The Rust equivalent of upstream's `Highlighter` ABC.
81///
82/// This is the primary *plugin* seam for the first slice: `rich-ext` registers
83/// [`Highlighter`]s onto a [`Console`] without the core knowing they exist.
84pub trait Highlighter {
85    /// Inspect `text` and apply any style spans in place.
86    fn highlight(&self, text: &mut Text);
87}
88
89/// Evidence for an optional output protocol; inference is not confirmation.
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum Support {
92    Unsupported,
93    Inferred,
94    Confirmed,
95}
96
97/// Immutable destination capabilities supplied by an extension. No detection or I/O.
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub struct TargetCapabilities {
100    pub width: usize,
101    pub height: usize,
102    pub color_system: Option<crate::color::ColorSystem>,
103    pub interactive: bool,
104    pub unicode: bool,
105    pub hyperlinks: bool,
106    pub sixel: Support,
107}
108
109/// Optional context shared by nested renderables without changing their protocol.
110pub trait RenderEnvironment: Send + Sync {
111    fn capabilities(&self) -> TargetCapabilities;
112}
113
114/// Attach/query a per-console immutable extension environment.
115pub trait ConsoleEnvironment {
116    fn set_render_environment(&mut self, value: Option<std::sync::Arc<dyn RenderEnvironment>>);
117    fn render_environment(&self) -> Option<&dyn RenderEnvironment>;
118}