mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 22:31:47 +00:00
Add optimized best_fit_parenthesize
IR (#7475)
This commit is contained in:
parent
28b48ab902
commit
6a4dbd622b
11 changed files with 413 additions and 96 deletions
|
@ -1451,6 +1451,150 @@ impl<Context> std::fmt::Debug for Group<'_, Context> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Content that may get parenthesized if it exceeds the configured line width but only if the parenthesized
|
||||
/// layout doesn't exceed the line width too, in which case it falls back to the flat layout.
|
||||
///
|
||||
/// This IR is identical to the following [`best_fitting`] layout but is implemented as custom IR for
|
||||
/// best performance.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ruff_formatter::prelude::*;
|
||||
/// # use ruff_formatter::format_args;
|
||||
///
|
||||
/// let format_expression = format_with(|f: &mut Formatter<SimpleFormatContext>| token("A long string").fmt(f));
|
||||
/// let _ = best_fitting![
|
||||
/// // ---------------------------------------------------------------------
|
||||
/// // Variant 1:
|
||||
/// // Try to fit the expression without any parentheses
|
||||
/// group(&format_expression),
|
||||
/// // ---------------------------------------------------------------------
|
||||
/// // Variant 2:
|
||||
/// // Try to fit the expression by adding parentheses and indenting the expression.
|
||||
/// group(&format_args![
|
||||
/// token("("),
|
||||
/// soft_block_indent(&format_expression),
|
||||
/// token(")")
|
||||
/// ])
|
||||
/// .should_expand(true),
|
||||
/// // ---------------------------------------------------------------------
|
||||
/// // Variant 3: Fallback, no parentheses
|
||||
/// // Expression doesn't fit regardless of adding the parentheses. Remove the parentheses again.
|
||||
/// group(&format_expression).should_expand(true)
|
||||
/// ]
|
||||
/// // Measure all lines, to avoid that the printer decides that this fits right after hitting
|
||||
/// // the `(`.
|
||||
/// .with_mode(BestFittingMode::AllLines) ;
|
||||
/// ```
|
||||
///
|
||||
/// The element breaks from left-to-right because it uses the unintended version as *expanded* layout, the same as the above showed best fitting example.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ### Content that fits into the configured line width.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ruff_formatter::prelude::*;
|
||||
/// # use ruff_formatter::{format, PrintResult, write};
|
||||
///
|
||||
/// # fn main() -> FormatResult<()> {
|
||||
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
|
||||
/// write!(f, [
|
||||
/// token("aLongerVariableName = "),
|
||||
/// best_fit_parenthesize(&token("'a string that fits into the configured line width'"))
|
||||
/// ])
|
||||
/// })])?;
|
||||
///
|
||||
/// assert_eq!(formatted.print()?.as_code(), "aLongerVariableName = 'a string that fits into the configured line width'");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ### Content that fits parenthesized
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ruff_formatter::prelude::*;
|
||||
/// # use ruff_formatter::{format, PrintResult, write};
|
||||
///
|
||||
/// # fn main() -> FormatResult<()> {
|
||||
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
|
||||
/// write!(f, [
|
||||
/// token("aLongerVariableName = "),
|
||||
/// best_fit_parenthesize(&token("'a string that exceeds configured line width but fits parenthesized'"))
|
||||
/// ])
|
||||
/// })])?;
|
||||
///
|
||||
/// assert_eq!(formatted.print()?.as_code(), "aLongerVariableName = (\n\t'a string that exceeds configured line width but fits parenthesized'\n)");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ### Content that exceeds the line width, parenthesized or not
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ruff_formatter::prelude::*;
|
||||
/// # use ruff_formatter::{format, PrintResult, write};
|
||||
///
|
||||
/// # fn main() -> FormatResult<()> {
|
||||
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
|
||||
/// write!(f, [
|
||||
/// token("aLongerVariableName = "),
|
||||
/// best_fit_parenthesize(&token("'a string that exceeds the configured line width and even parenthesizing doesn't make it fit'"))
|
||||
/// ])
|
||||
/// })])?;
|
||||
///
|
||||
/// assert_eq!(formatted.print()?.as_code(), "aLongerVariableName = 'a string that exceeds the configured line width and even parenthesizing doesn't make it fit'");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn best_fit_parenthesize<Context>(
|
||||
content: &impl Format<Context>,
|
||||
) -> BestFitParenthesize<Context> {
|
||||
BestFitParenthesize {
|
||||
content: Argument::new(content),
|
||||
group_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BestFitParenthesize<'a, Context> {
|
||||
content: Argument<'a, Context>,
|
||||
group_id: Option<GroupId>,
|
||||
}
|
||||
|
||||
impl<Context> BestFitParenthesize<'_, Context> {
|
||||
/// Optional ID that can be used in conditional content that supports [`Condition`] to gate content
|
||||
/// depending on whether the parentheses are rendered (flat: no parentheses, expanded: parentheses).
|
||||
#[must_use]
|
||||
pub fn with_group_id(mut self, group_id: Option<GroupId>) -> Self {
|
||||
self.group_id = group_id;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context> Format<Context> for BestFitParenthesize<'_, Context> {
|
||||
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
|
||||
f.write_element(FormatElement::Tag(StartBestFitParenthesize {
|
||||
id: self.group_id,
|
||||
}));
|
||||
|
||||
Arguments::from(&self.content).fmt(f)?;
|
||||
|
||||
f.write_element(FormatElement::Tag(EndBestFitParenthesize));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context> std::fmt::Debug for BestFitParenthesize<'_, Context> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BestFitParenthesize")
|
||||
.field("group_id", &self.group_id)
|
||||
.field("content", &"{{content}}")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `condition` for the group. The element will behave as a regular group if `condition` is met,
|
||||
/// and as *ungrouped* content if the condition is not met.
|
||||
///
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue