use crate::prelude::*; use std::cell::OnceCell; use std::marker::PhantomData; use crate::Buffer; /// Utility trait that allows memorizing the output of a [`Format`]. /// Useful to avoid re-formatting the same object twice. pub trait MemoizeFormat { /// Returns a formattable object that memoizes the result of `Format` by cloning. /// Mainly useful if the same sub-tree can appear twice in the formatted output because it's /// used inside of `if_group_breaks` or `if_group_fits_single_line`. /// /// ``` /// use std::cell::Cell; /// use ruff_formatter::{format, write}; /// use ruff_formatter::prelude::*; /// use ruff_text_size::{Ranged, TextSize}; /// /// struct MyFormat { /// value: Cell /// } /// /// impl MyFormat { /// pub fn new() -> Self { /// Self { value: Cell::new(1) } /// } /// } /// /// impl Format for MyFormat { /// fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { /// let value = self.value.get(); /// self.value.set(value + 1); /// /// write!(f, [text(&std::format!("Formatted {value} times."), None)]) /// } /// } /// /// # fn main() -> FormatResult<()> { /// let normal = MyFormat::new(); /// /// // Calls `format` every time the object gets formatted /// assert_eq!( /// "Formatted 1 times. Formatted 2 times.", /// format!(SimpleFormatContext::default(), [normal, space(), normal])?.print()?.as_code() /// ); /// /// // Memoized memoizes the result and calls `format` only once. /// let memoized = normal.memoized(); /// assert_eq!( /// "Formatted 3 times. Formatted 3 times.", /// format![SimpleFormatContext::default(), [memoized, space(), memoized]]?.print()?.as_code() /// ); /// # Ok(()) /// # } /// ``` fn memoized(self) -> Memoized where Self: Sized + Format, { Memoized::new(self) } } impl MemoizeFormat for T where T: Format {} /// Memoizes the output of its inner [`Format`] to avoid re-formatting a potential expensive object. #[derive(Debug)] pub struct Memoized { inner: F, memory: OnceCell>>, options: PhantomData, } impl Memoized where F: Format, { fn new(inner: F) -> Self { Self { inner, memory: OnceCell::new(), options: PhantomData, } } /// Gives access to the memoized content. /// /// Performs the formatting if the content hasn't been formatted at this point. /// /// # Example /// /// Inspect if some memoized content breaks. /// /// ```rust /// use std::cell::Cell; /// use ruff_formatter::{format, write}; /// use ruff_formatter::prelude::*; /// use ruff_text_size::{Ranged, TextSize}; /// /// #[derive(Default)] /// struct Counter { /// value: Cell /// } /// /// impl Format for Counter { /// fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { /// let current = self.value.get(); /// /// write!(f, [ /// token("Count:"), /// space(), /// text(&std::format!("{current}"), None), /// hard_line_break() /// ])?; /// /// self.value.set(current + 1); /// Ok(()) /// } /// } /// /// # fn main() -> FormatResult<()> { /// let content = format_with(|f| { /// let mut counter = Counter::default().memoized(); /// let counter_content = counter.inspect(f)?; /// /// if counter_content.will_break() { /// write!(f, [token("Counter:"), block_indent(&counter)]) /// } else { /// write!(f, [token("Counter:"), counter]) /// }?; /// /// write!(f, [counter]) /// }); /// /// /// let formatted = format!(SimpleFormatContext::default(), [content])?; /// assert_eq!("Counter:\n\tCount: 0\nCount: 0\n", formatted.print()?.as_code()); /// # Ok(()) /// # } /// ``` pub fn inspect(&mut self, f: &mut Formatter) -> FormatResult<&[FormatElement]> { let result = self.memory.get_or_init(|| f.intern(&self.inner)); match result.as_ref() { Ok(Some(FormatElement::Interned(interned))) => Ok(&**interned), Ok(Some(other)) => Ok(std::slice::from_ref(other)), Ok(None) => Ok(&[]), Err(error) => Err(*error), } } } impl Format for Memoized where F: Format, { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { let result = self.memory.get_or_init(|| f.intern(&self.inner)); match result { Ok(Some(elements)) => { f.write_element(elements.clone()); Ok(()) } Ok(None) => Ok(()), Err(err) => Err(*err), } } }