-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e04c281
commit 6dc9fa3
Showing
4 changed files
with
181 additions
and
12 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#![allow(dead_code)] | ||
|
||
use crate::prelude::*; | ||
use std::cell::RefCell; | ||
use std::marker::PhantomData; | ||
use std::ops::Deref; | ||
|
||
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<Context> { | ||
/// 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::TextSize; | ||
/// | ||
/// struct MyFormat { | ||
/// value: Cell<u64> | ||
/// } | ||
/// | ||
/// impl MyFormat { | ||
/// pub fn new() -> Self { | ||
/// Self { value: Cell::new(1) } | ||
/// } | ||
/// } | ||
/// | ||
/// impl Format<SimpleFormatContext> for MyFormat { | ||
/// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> { | ||
/// let value = self.value.get(); | ||
/// self.value.set(value + 1); | ||
/// | ||
/// write!(f, [dynamic_text(&std::format!("Formatted {value} times."), TextSize::from(0))]) | ||
/// } | ||
/// } | ||
/// | ||
/// # fn main() -> FormatResult<()> { | ||
/// let normal = MyFormat::new(); | ||
/// | ||
/// // Calls `format` for everytime 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<Self, Context> | ||
where | ||
Self: Sized + Format<Context>, | ||
{ | ||
Memoized::new(self) | ||
} | ||
} | ||
|
||
impl<T, Context> MemoizeFormat<Context> for T where T: Format<Context> {} | ||
|
||
/// Memoizes the output of its inner [Format] to avoid re-formatting a potential expensive object. | ||
#[derive(Debug)] | ||
pub struct Memoized<F, Context> { | ||
inner: F, | ||
memory: RefCell<Option<FormatResult<Option<FormatElement>>>>, | ||
options: PhantomData<Context>, | ||
} | ||
|
||
impl<F, Context> Memoized<F, Context> | ||
where | ||
F: Format<Context>, | ||
{ | ||
fn new(inner: F) -> Self { | ||
Self { | ||
inner, | ||
memory: RefCell::new(None), | ||
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::TextSize; | ||
/// | ||
/// #[derive(Default)] | ||
/// struct Counter { | ||
/// value: Cell<u64> | ||
/// } | ||
/// | ||
/// impl Format<SimpleFormatContext> for Counter { | ||
/// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> { | ||
/// let current = self.value.get(); | ||
/// | ||
/// write!(f, [ | ||
/// text("Count:"), | ||
/// space(), | ||
/// dynamic_text(&std::format!("{current}"), TextSize::default()), | ||
/// 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, [text("Counter:"), block_indent(&counter)]) | ||
/// } else { | ||
/// write!(f, [text("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<Context>) -> FormatResult<&[FormatElement]> { | ||
let result = self | ||
.memory | ||
.get_mut() | ||
.get_or_insert_with(|| f.intern(&self.inner)); | ||
|
||
match result.as_ref() { | ||
Ok(Some(FormatElement::Interned(interned))) => Ok(interned.deref()), | ||
Ok(Some(other)) => Ok(std::slice::from_ref(other)), | ||
Ok(None) => Ok(&[]), | ||
Err(error) => Err(*error), | ||
} | ||
} | ||
} | ||
|
||
impl<F, Context> Format<Context> for Memoized<F, Context> | ||
where | ||
F: Format<Context>, | ||
{ | ||
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> { | ||
let mut memory = self.memory.borrow_mut(); | ||
let result = memory.get_or_insert_with(|| f.intern(&self.inner)); | ||
|
||
match result { | ||
Ok(Some(elements)) => { | ||
f.write_element(elements.clone())?; | ||
|
||
Ok(()) | ||
} | ||
Ok(None) => Ok(()), | ||
Err(err) => Err(*err), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters