1mod export;
17mod lowering;
18mod position;
19mod target;
20mod tree;
21
22pub use export::{EXPORT_BYTES_MAX, export};
23pub use tree::{Align, Block, Check, Construct, Inline, Laid, ListItem, Order, Preview};
24pub use tree::{ReplyState, Row, Spacing};
25
26use kernel::AxError;
27
28use crate::encoding::Encoding;
29use crate::format::Format;
30use crate::layout;
31use crate::span::{Span, offset};
32use crate::window::{Lifted, Window, cut};
33
34pub fn preview(encoding: Encoding, lifted: Lifted<'_>, viewport: Span) -> Result<Preview, AxError> {
41 match encoding {
42 Encoding::Utf16Le | Encoding::Utf16Be => Ok(Preview::Unsupported { encoding }),
43 Encoding::Utf8 | Encoding::Utf8Bom => laid(encoding, lifted, viewport).map(Preview::Laid),
44 }
45}
46
47pub fn reply(text: &str, state: ReplyState) -> Result<Laid, AxError> {
57 let bytes = text.as_bytes();
58 let size = match state {
59 ReplyState::Settled => offset(bytes.len()),
60 ReplyState::Streaming => layout::closed(bytes),
61 };
62 laid(
63 Encoding::Utf8,
64 Lifted { at: 0, bytes, size },
65 Span::ordered(0, size),
66 )
67}
68
69fn laid(encoding: Encoding, lifted: Lifted<'_>, viewport: Span) -> Result<Laid, AxError> {
71 let window = settled(cut(encoding, lifted, viewport)?, lifted.size);
72 let blocks = lowering::blocks(&window.text, window.span.start());
73 Ok(Laid {
74 span: window.span,
75 blocks,
76 })
77}
78
79fn settled(window: Window, size: u64) -> Window {
84 if window.span.end() >= size {
85 return window;
86 }
87 let ends = layout::blocks(Format::Markdown, window.text.as_bytes());
88 let Some(end) = ends
89 .iter()
90 .rev()
91 .nth(1)
92 .map(|block| block.end())
93 .filter(|end| *end > 0)
94 else {
95 return window;
96 };
97 let kept = usize::try_from(end)
98 .ok()
99 .and_then(|end| window.text.get(..end));
100 match kept {
101 Some(text) => Window {
102 span: Span::ordered(
103 window.span.start(),
104 window.span.start().saturating_add(offset(text.len())),
105 ),
106 text: text.to_owned(),
107 },
108 None => window,
109 }
110}
111
112#[cfg(test)]
113#[allow(
114 clippy::unwrap_used,
115 clippy::expect_used,
116 clippy::panic,
117 clippy::indexing_slicing,
118 clippy::arithmetic_side_effects,
119 reason = "test code"
120)]
121mod tests;