mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 21:05:08 +00:00
Add rome_formatter
fork as ruff_formatter
(#2872)
The Ruff autoformatter is going to be based on an intermediate representation (IR) formatted via [Wadler's algorithm](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf). This is architecturally similar to [Rome](https://github.com/rome/tools), Prettier, [Skip](https://github.com/skiplang/skip/blob/master/src/tools/printer/printer.sk), and others. This PR adds a fork of the `rome_formatter` crate from [Rome](https://github.com/rome/tools), renamed here to `ruff_formatter`, which provides generic definitions for a formatter IR as well as a generic IR printer. (We've also pulled in `rome_rowan`, `rome_text_size`, and `rome_text_edit`, though some of these will be removed in future PRs.) Why fork? `rome_formatter` contains code that's specific to Rome's AST representation (e.g., it relies on a fork of rust-analyzer's `rowan`), and we'll likely want to support different abstractions and formatting capabilities (there are already a few changes coming in future PRs). Once we've dropped `ruff_rowan` and trimmed down `ruff_formatter` to the code we currently need, it's also not a huge surface area to maintain and update.
This commit is contained in:
parent
ac028cd9f8
commit
3ef1c2e303
83 changed files with 27547 additions and 1 deletions
178
crates/ruff_formatter/src/format_extensions.rs
Normal file
178
crates/ruff_formatter/src/format_extensions.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
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_rowan::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_rowan::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),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue