1use std::{marker::PhantomData, ptr::NonNull};
7
8use crate::{
9 alloc::{Allocator, Bytes, Object},
10 error::{Error, Result, from_result},
11 ffi,
12 selection::Selection,
13 terminal::Terminal,
14};
15
16#[derive(Debug)]
18pub struct Formatter<'t, 'alloc: 'cb, 'cb: 't> {
19 inner: Object<'alloc, ffi::FormatterImpl>,
20 _terminal: PhantomData<&'t Terminal<'alloc, 'cb>>,
21}
22
23#[derive(Debug)]
25pub struct FormatterOptions<'t, 's> {
26 inner: ffi::FormatterTerminalOptions,
27 _phan: PhantomData<&'s Selection<'t>>,
28}
29impl<'t, 's> FormatterOptions<'t, 's> {
30 pub fn new() -> Self {
32 Self {
33 inner: ffi::FormatterTerminalOptions {
34 extra: ffi::FormatterTerminalExtra {
35 screen: ffi::FormatterScreenExtra {
36 ..ffi::sized!(ffi::FormatterScreenExtra)
37 },
38 ..ffi::sized!(ffi::FormatterTerminalExtra)
39 },
40 ..ffi::sized!(ffi::FormatterTerminalOptions)
41 },
42 _phan: PhantomData,
43 }
44 }
45 pub fn with_format(mut self, value: Format) -> Self {
47 self.inner.emit = value.into();
48 self
49 }
50 pub fn with_unwrap(mut self, value: bool) -> Self {
52 self.inner.unwrap = value;
53 self
54 }
55 pub fn with_trim(mut self, value: bool) -> Self {
57 self.inner.trim = value;
58 self
59 }
60 pub fn with_selection(mut self, value: &'s Selection<'t>) -> Self {
65 self.inner.selection = &value.inner;
66 self
67 }
68
69 pub fn with_palette(mut self, value: bool) -> Self {
73 self.inner.extra.palette = value;
74 self
75 }
76 pub fn with_modes(mut self, value: bool) -> Self {
78 self.inner.extra.modes = value;
79 self
80 }
81 pub fn with_scrolling_region(mut self, value: bool) -> Self {
83 self.inner.extra.scrolling_region = value;
84 self
85 }
86 pub fn with_tabstops(mut self, value: bool) -> Self {
88 self.inner.extra.tabstops = value;
89 self
90 }
91 pub fn with_pwd(mut self, value: bool) -> Self {
93 self.inner.extra.pwd = value;
94 self
95 }
96 pub fn with_keyboard(mut self, value: bool) -> Self {
98 self.inner.extra.keyboard = value;
99 self
100 }
101
102 pub fn with_cursor(mut self, value: bool) -> Self {
106 self.inner.extra.screen.cursor = value;
107 self
108 }
109 pub fn with_style(mut self, value: bool) -> Self {
111 self.inner.extra.screen.style = value;
112 self
113 }
114 pub fn with_hyperlink(mut self, value: bool) -> Self {
116 self.inner.extra.screen.hyperlink = value;
117 self
118 }
119 pub fn with_protection(mut self, value: bool) -> Self {
121 self.inner.extra.screen.protection = value;
122 self
123 }
124 pub fn with_kitty_keyboard(mut self, value: bool) -> Self {
126 self.inner.extra.screen.kitty_keyboard = value;
127 self
128 }
129 pub fn with_charsets(mut self, value: bool) -> Self {
131 self.inner.extra.screen.charsets = value;
132 self
133 }
134}
135
136impl<'t, 'alloc: 'cb, 'cb: 't> Formatter<'t, 'alloc, 'cb> {
137 pub fn new(
139 terminal: &'t Terminal<'alloc, 'cb>,
140 opts: FormatterOptions<'t, '_>,
141 ) -> Result<Self> {
142 unsafe { Self::new_inner(std::ptr::null(), terminal, opts) }
144 }
145
146 pub fn new_with_alloc<'ctx: 'alloc>(
151 alloc: &'alloc Allocator<'ctx>,
152 terminal: &'t Terminal<'alloc, 'cb>,
153 opts: FormatterOptions,
154 ) -> Result<Self> {
155 unsafe { Self::new_inner(alloc.to_raw(), terminal, opts) }
157 }
158
159 unsafe fn new_inner(
160 alloc: *const ffi::Allocator,
161 terminal: &'t Terminal<'alloc, 'cb>,
162 opts: FormatterOptions,
163 ) -> Result<Self> {
164 let mut raw: ffi::Formatter = std::ptr::null_mut();
165
166 let result = unsafe {
167 ffi::ghostty_formatter_terminal_new(
168 alloc,
169 &raw mut raw,
170 terminal.inner.as_raw(),
171 opts.inner,
172 )
173 };
174 from_result(result)?;
175
176 Ok(Self {
177 inner: Object::new(raw)?,
178 _terminal: PhantomData,
179 })
180 }
181
182 pub fn format_alloc<'a, 'ctx: 'a>(
187 &mut self,
188 alloc: Option<&'a Allocator<'ctx>>,
189 ) -> Result<Bytes<'a>> {
190 let alloc = if let Some(alloc) = alloc {
191 alloc.to_raw()
192 } else {
193 std::ptr::null()
194 };
195
196 let mut bytes = std::ptr::null_mut();
197 let mut len = 0usize;
198 let result = unsafe {
199 ffi::ghostty_formatter_format_alloc(
200 self.inner.as_raw(),
201 alloc,
202 std::ptr::from_mut(&mut bytes),
203 std::ptr::from_mut(&mut len),
204 )
205 };
206 from_result(result)?;
207
208 let ptr = NonNull::new(bytes).ok_or(Error::OutOfMemory)?;
209 Ok(unsafe { Bytes::from_raw_parts(ptr, len, alloc) })
210 }
211
212 pub fn format_buf(&mut self, buf: &mut [u8]) -> Result<usize> {
218 let mut len = 0usize;
219 let result = unsafe {
220 ffi::ghostty_formatter_format_buf(
221 self.inner.as_raw(),
222 std::ptr::from_mut(buf).cast(),
223 buf.len(),
224 std::ptr::from_mut(&mut len),
225 )
226 };
227 from_result(result)?;
228 Ok(len)
229 }
230
231 pub fn format_len(&mut self) -> Result<usize> {
236 let mut len = 0usize;
237 let result = unsafe {
238 ffi::ghostty_formatter_format_buf(
239 self.inner.as_raw(),
240 std::ptr::null_mut(),
241 0,
242 std::ptr::from_mut(&mut len),
243 )
244 };
245 match from_result(result) {
247 Err(Error::OutOfSpace { .. }) => Ok(len),
248 Err(e) => Err(e),
249 Ok(()) => Err(Error::InvalidValue),
250 }
251 }
252}
253
254impl Drop for Formatter<'_, '_, '_> {
255 fn drop(&mut self) {
256 unsafe { ffi::ghostty_formatter_free(self.inner.as_raw()) }
257 }
258}
259
260#[repr(u32)]
262#[derive(Debug, Clone, Copy, PartialEq, Eq, int_enum::IntEnum)]
263pub enum Format {
264 Plain = ffi::FormatterFormat::PLAIN,
266 Vt = ffi::FormatterFormat::VT,
268 Html = ffi::FormatterFormat::HTML,
270}