Extend formatter IR to support Black's expression formatting (#5596)

This commit is contained in:
Micha Reiser 2023-07-11 13:20:04 +02:00 committed by GitHub
parent 212fd86bf0
commit d30e9125eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 576 additions and 56 deletions

View file

@ -1410,7 +1410,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
impl<Context> std::fmt::Debug for Group<'_, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GroupElements")
f.debug_struct("Group")
.field("group_id", &self.group_id)
.field("should_expand", &self.should_expand)
.field("content", &"{{content}}")
@ -1418,6 +1418,135 @@ impl<Context> std::fmt::Debug for Group<'_, Context> {
}
}
/// 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.
///
/// ## Examples
///
/// Only expand before operators if the parentheses are necessary.
///
/// ```
/// # use ruff_formatter::prelude::*;
/// # use ruff_formatter::{format, format_args, LineWidth, SimpleFormatOptions};
///
/// # fn main() -> FormatResult<()> {
/// use ruff_formatter::Formatted;
/// let content = format_with(|f| {
/// let parentheses_id = f.group_id("parentheses");
/// group(&format_args![
/// if_group_breaks(&text("(")),
/// indent_if_group_breaks(&format_args![
/// soft_line_break(),
/// conditional_group(&format_args![
/// text("'aaaaaaa'"),
/// soft_line_break_or_space(),
/// text("+"),
/// space(),
/// fits_expanded(&conditional_group(&format_args![
/// text("["),
/// soft_block_indent(&format_args![
/// text("'Good morning!',"),
/// soft_line_break_or_space(),
/// text("'How are you?'"),
/// ]),
/// text("]"),
/// ], tag::Condition::if_group_fits_on_line(parentheses_id))),
/// soft_line_break_or_space(),
/// text("+"),
/// space(),
/// conditional_group(&format_args![
/// text("'bbbb'"),
/// soft_line_break_or_space(),
/// text("and"),
/// space(),
/// text("'c'")
/// ], tag::Condition::if_group_fits_on_line(parentheses_id))
/// ], tag::Condition::if_breaks()),
/// ], parentheses_id),
/// soft_line_break(),
/// if_group_breaks(&text(")"))
/// ])
/// .with_group_id(Some(parentheses_id))
/// .fmt(f)
/// });
///
/// let formatted = format!(SimpleFormatContext::default(), [content])?;
/// let document = formatted.into_document();
///
/// // All content fits
/// let all_fits = Formatted::new(document.clone(), SimpleFormatContext::new(SimpleFormatOptions {
/// line_width: LineWidth::try_from(65).unwrap(),
/// ..SimpleFormatOptions::default()
/// }));
///
/// assert_eq!(
/// "'aaaaaaa' + ['Good morning!', 'How are you?'] + 'bbbb' and 'c'",
/// all_fits.print()?.as_code()
/// );
///
/// // The parentheses group fits, because it can expand the list,
/// let list_expanded = Formatted::new(document.clone(), SimpleFormatContext::new(SimpleFormatOptions {
/// line_width: LineWidth::try_from(21).unwrap(),
/// ..SimpleFormatOptions::default()
/// }));
///
/// assert_eq!(
/// "'aaaaaaa' + [\n\t'Good morning!',\n\t'How are you?'\n] + 'bbbb' and 'c'",
/// list_expanded.print()?.as_code()
/// );
///
/// // It is necessary to split all groups to fit the content
/// let all_expanded = Formatted::new(document, SimpleFormatContext::new(SimpleFormatOptions {
/// line_width: LineWidth::try_from(11).unwrap(),
/// ..SimpleFormatOptions::default()
/// }));
///
/// assert_eq!(
/// "(\n\t'aaaaaaa'\n\t+ [\n\t\t'Good morning!',\n\t\t'How are you?'\n\t]\n\t+ 'bbbb'\n\tand 'c'\n)",
/// all_expanded.print()?.as_code()
/// );
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn conditional_group<Content, Context>(
content: &Content,
condition: Condition,
) -> ConditionalGroup<Context>
where
Content: Format<Context>,
{
ConditionalGroup {
content: Argument::new(content),
condition,
}
}
#[derive(Clone)]
pub struct ConditionalGroup<'content, Context> {
content: Argument<'content, Context>,
condition: Condition,
}
impl<Context> Format<Context> for ConditionalGroup<'_, Context> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::Tag(StartConditionalGroup(
tag::ConditionalGroup::new(self.condition),
)))?;
f.write_fmt(Arguments::from(&self.content))?;
f.write_element(FormatElement::Tag(EndConditionalGroup))
}
}
impl<Context> std::fmt::Debug for ConditionalGroup<'_, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConditionalGroup")
.field("condition", &self.condition)
.field("content", &"{{content}}")
.finish()
}
}
/// IR element that forces the parent group to print in expanded mode.
///
/// Has no effect if used outside of a group or element that introduce implicit groups (fill element).
@ -1841,6 +1970,86 @@ impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
}
}
/// Changes the definition of *fits* for `content`. Instead of measuring it in *flat*, measure it with
/// all line breaks expanded and test if no line exceeds the line width. The [`FitsExpanded`] acts
/// as a expands boundary similar to best fitting, meaning that a [hard_line_break] will not cause the parent group to expand.
///
/// Useful in conjunction with a group with a condition.
///
/// ## Examples
/// The outer group with the binary expression remains *flat* regardless of the array expression
/// that spans multiple lines.
///
/// ```
/// # use ruff_formatter::{format, format_args, LineWidth, SimpleFormatOptions, write};
/// # use ruff_formatter::prelude::*;
///
/// # fn main() -> FormatResult<()> {
/// let content = format_with(|f| {
/// let group_id = f.group_id("header");
///
/// write!(f, [
/// group(&format_args![
/// text("a"),
/// soft_line_break_or_space(),
/// text("+"),
/// space(),
/// fits_expanded(&group(&format_args![
/// text("["),
/// soft_block_indent(&format_args![
/// text("a,"), space(), text("# comment"), expand_parent(), soft_line_break_or_space(),
/// text("b")
/// ]),
/// text("]")
/// ]))
/// ]),
/// ])
/// });
///
/// let formatted = format!(SimpleFormatContext::default(), [content])?;
///
/// assert_eq!(
/// "a + [\n\ta, # comment\n\tb\n]",
/// formatted.print()?.as_code()
/// );
/// # Ok(())
/// # }
/// ```
pub fn fits_expanded<Content, Context>(content: &Content) -> FitsExpanded<Context>
where
Content: Format<Context>,
{
FitsExpanded {
content: Argument::new(content),
condition: None,
}
}
#[derive(Clone)]
pub struct FitsExpanded<'a, Context> {
content: Argument<'a, Context>,
condition: Option<Condition>,
}
impl<Context> FitsExpanded<'_, Context> {
/// Sets a `condition` to when the content should fit in expanded mode. The content uses the regular fits
/// definition if the `condition` is not met.
pub fn with_condition(mut self, condition: Option<Condition>) -> Self {
self.condition = condition;
self
}
}
impl<Context> Format<Context> for FitsExpanded<'_, Context> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::Tag(StartFitsExpanded(
tag::FitsExpanded::new().with_condition(self.condition),
)))?;
f.write_fmt(Arguments::from(&self.content))?;
f.write_element(FormatElement::Tag(EndFitsExpanded))
}
}
/// Utility for formatting some content with an inline lambda function.
#[derive(Copy, Clone)]
pub struct FormatWith<Context, T> {

View file

@ -1,5 +1,5 @@
use super::tag::Tag;
use crate::format_element::tag::DedentMode;
use crate::format_element::tag::{Condition, DedentMode};
use crate::prelude::tag::GroupMode;
use crate::prelude::*;
use crate::printer::LineEnding;
@ -33,12 +33,17 @@ impl Document {
#[derive(Debug)]
enum Enclosing<'a> {
Group(&'a tag::Group),
ConditionalGroup(&'a tag::ConditionalGroup),
FitsExpanded(&'a tag::FitsExpanded),
BestFitting,
}
fn expand_parent(enclosing: &[Enclosing]) {
if let Some(Enclosing::Group(group)) = enclosing.last() {
group.propagate_expand();
match enclosing.last() {
Some(Enclosing::Group(group)) => group.propagate_expand(),
Some(Enclosing::ConditionalGroup(group)) => group.propagate_expand(),
Some(Enclosing::FitsExpanded(fits_expanded)) => fits_expanded.propagate_expand(),
_ => {}
}
}
@ -58,6 +63,14 @@ impl Document {
Some(Enclosing::Group(group)) => !group.mode().is_flat(),
_ => false,
},
FormatElement::Tag(Tag::StartConditionalGroup(group)) => {
enclosing.push(Enclosing::ConditionalGroup(group));
false
}
FormatElement::Tag(Tag::EndConditionalGroup) => match enclosing.pop() {
Some(Enclosing::ConditionalGroup(group)) => !group.mode().is_flat(),
_ => false,
},
FormatElement::Interned(interned) => match checked_interned.get(interned) {
Some(interned_expands) => *interned_expands,
None => {
@ -79,6 +92,16 @@ impl Document {
enclosing.pop();
continue;
}
FormatElement::Tag(Tag::StartFitsExpanded(fits_expanded)) => {
enclosing.push(Enclosing::FitsExpanded(fits_expanded));
false
}
FormatElement::Tag(Tag::EndFitsExpanded) => {
enclosing.pop();
// Fits expanded acts as a boundary
expands = false;
continue;
}
FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::SourceCodeSlice {
@ -441,6 +464,29 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
}
}
StartConditionalGroup(group) => {
write!(
f,
[
text("conditional_group(condition:"),
space(),
group.condition(),
text(","),
space()
]
)?;
match group.mode() {
GroupMode::Flat => {}
GroupMode::Expand => {
write!(f, [text("expand: true,"), space()])?;
}
GroupMode::Propagated => {
write!(f, [text("expand: propagated,"), space()])?;
}
}
}
StartIndentIfGroupBreaks(id) => {
write!(
f,
@ -491,6 +537,28 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!(f, [text("fill(")])?;
}
StartFitsExpanded(tag::FitsExpanded {
condition,
propagate_expand,
}) => {
write!(f, [text("fits_expanded(propagate_expand:"), space()])?;
if propagate_expand.get() {
write!(f, [text("true")])?;
} else {
write!(f, [text("false")])?;
}
write!(f, [text(","), space()])?;
if let Some(condition) = condition {
write!(
f,
[text("condition:"), space(), condition, text(","), space()]
)?;
}
}
StartEntry => {
// handled after the match for all start tags
}
@ -503,8 +571,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
| EndAlign
| EndIndent
| EndGroup
| EndConditionalGroup
| EndLineSuffix
| EndDedent
| EndFitsExpanded
| EndVerbatim => {
write!(f, [ContentArrayEnd, text(")")])?;
}
@ -658,6 +728,31 @@ impl FormatElements for [FormatElement] {
}
}
impl Format<IrFormatContext<'_>> for Condition {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
match (self.mode, self.group_id) {
(PrintMode::Flat, None) => write!(f, [text("if_fits_on_line")]),
(PrintMode::Flat, Some(id)) => write!(
f,
[
text("if_group_fits_on_line("),
dynamic_text(&std::format!("\"{id:?}\""), None),
text(")")
]
),
(PrintMode::Expanded, None) => write!(f, [text("if_breaks")]),
(PrintMode::Expanded, Some(id)) => write!(
f,
[
text("if_group_breaks("),
dynamic_text(&std::format!("\"{id:?}\""), None),
text(")")
]
),
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;

View file

@ -33,6 +33,16 @@ pub enum Tag {
StartGroup(Group),
EndGroup,
/// Creates a logical group similar to [`Tag::StartGroup`] but only if the condition is met.
/// This is an optimized representation for (assuming the content should only be grouped if another group fits):
///
/// ```text
/// if_group_breaks(content, other_group_id),
/// if_group_fits_on_line(group(&content), other_group_id)
/// ```
StartConditionalGroup(ConditionalGroup),
EndConditionalGroup,
/// Allows to specify content that gets printed depending on whatever the enclosing group
/// is printed on a single line or multiple lines. See [crate::builders::if_group_breaks] for examples.
StartConditionalContent(Condition),
@ -67,6 +77,9 @@ pub enum Tag {
/// See [crate::builders::labelled] for documentation.
StartLabelled(LabelId),
EndLabelled,
StartFitsExpanded(FitsExpanded),
EndFitsExpanded,
}
impl Tag {
@ -77,7 +90,8 @@ impl Tag {
Tag::StartIndent
| Tag::StartAlign(_)
| Tag::StartDedent(_)
| Tag::StartGroup { .. }
| Tag::StartGroup(_)
| Tag::StartConditionalGroup(_)
| Tag::StartConditionalContent(_)
| Tag::StartIndentIfGroupBreaks(_)
| Tag::StartFill
@ -85,6 +99,7 @@ impl Tag {
| Tag::StartLineSuffix
| Tag::StartVerbatim(_)
| Tag::StartLabelled(_)
| Tag::StartFitsExpanded(_)
)
}
@ -101,6 +116,7 @@ impl Tag {
StartAlign(_) | EndAlign => TagKind::Align,
StartDedent(_) | EndDedent => TagKind::Dedent,
StartGroup(_) | EndGroup => TagKind::Group,
StartConditionalGroup(_) | EndConditionalGroup => TagKind::ConditionalGroup,
StartConditionalContent(_) | EndConditionalContent => TagKind::ConditionalContent,
StartIndentIfGroupBreaks(_) | EndIndentIfGroupBreaks => TagKind::IndentIfGroupBreaks,
StartFill | EndFill => TagKind::Fill,
@ -108,6 +124,7 @@ impl Tag {
StartLineSuffix | EndLineSuffix => TagKind::LineSuffix,
StartVerbatim(_) | EndVerbatim => TagKind::Verbatim,
StartLabelled(_) | EndLabelled => TagKind::Labelled,
StartFitsExpanded { .. } | EndFitsExpanded => TagKind::FitsExpanded,
}
}
}
@ -122,6 +139,7 @@ pub enum TagKind {
Align,
Dedent,
Group,
ConditionalGroup,
ConditionalContent,
IndentIfGroupBreaks,
Fill,
@ -129,6 +147,7 @@ pub enum TagKind {
LineSuffix,
Verbatim,
Labelled,
FitsExpanded,
}
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
@ -150,6 +169,27 @@ impl GroupMode {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct FitsExpanded {
pub(crate) condition: Option<Condition>,
pub(crate) propagate_expand: Cell<bool>,
}
impl FitsExpanded {
pub fn new() -> Self {
Self::default()
}
pub fn with_condition(mut self, condition: Option<Condition>) -> Self {
self.condition = condition;
self
}
pub fn propagate_expand(&self) {
self.propagate_expand.set(true)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Group {
id: Option<GroupId>,
@ -189,6 +229,33 @@ impl Group {
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ConditionalGroup {
mode: Cell<GroupMode>,
condition: Condition,
}
impl ConditionalGroup {
pub fn new(condition: Condition) -> Self {
Self {
mode: Cell::new(GroupMode::Flat),
condition,
}
}
pub fn condition(&self) -> Condition {
self.condition
}
pub fn propagate_expand(&self) {
self.mode.set(GroupMode::Propagated)
}
pub fn mode(&self) -> GroupMode {
self.mode.get()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DedentMode {
/// Reduces the indent by a level (if the current indent is > 0)
@ -198,7 +265,7 @@ pub enum DedentMode {
Root,
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Condition {
/// - `Flat` -> Omitted if the enclosing group is a multiline group, printed for groups fitting on a single line
/// - `Expanded` -> Omitted if the enclosing group fits on a single line, printed if the group breaks over multiple lines.
@ -217,6 +284,34 @@ impl Condition {
}
}
pub fn if_fits_on_line() -> Self {
Self {
mode: PrintMode::Flat,
group_id: None,
}
}
pub fn if_group_fits_on_line(group_id: GroupId) -> Self {
Self {
mode: PrintMode::Flat,
group_id: Some(group_id),
}
}
pub fn if_breaks() -> Self {
Self {
mode: PrintMode::Expanded,
group_id: None,
}
}
pub fn if_group_breaks(group_id: GroupId) -> Self {
Self {
mode: PrintMode::Expanded,
group_id: Some(group_id),
}
}
pub fn with_group_id(mut self, id: Option<GroupId>) -> Self {
self.group_id = id;
self

View file

@ -7,6 +7,7 @@ mod stack;
use crate::format_element::document::Document;
use crate::format_element::tag::{Condition, GroupMode};
use crate::format_element::{BestFittingMode, BestFittingVariants, LineMode, PrintMode};
use crate::prelude::tag;
use crate::prelude::tag::{DedentMode, Tag, TagKind, VerbatimKind};
use crate::printer::call_stack::{
CallStack, FitsCallStack, PrintCallStack, PrintElementArgs, StackFrame,
@ -143,40 +144,26 @@ impl<'a> Printer<'a> {
}
FormatElement::Tag(StartGroup(group)) => {
let group_mode = match group.mode() {
GroupMode::Expand | GroupMode::Propagated => PrintMode::Expanded,
GroupMode::Flat => {
match args.mode() {
PrintMode::Flat if self.state.measured_group_fits => {
// A parent group has already verified that this group fits on a single line
// Thus, just continue in flat mode
PrintMode::Flat
}
// The printer is either in expanded mode or it's necessary to re-measure if the group fits
// because the printer printed a line break
_ => {
self.state.measured_group_fits = true;
// Measure to see if the group fits up on a single line. If that's the case,
// print the group in "flat" mode, otherwise continue in expanded mode
stack.push(TagKind::Group, args.with_print_mode(PrintMode::Flat));
let fits = self.fits(queue, stack)?;
stack.pop(TagKind::Group)?;
if fits {
PrintMode::Flat
} else {
PrintMode::Expanded
}
}
}
}
};
stack.push(TagKind::Group, args.with_print_mode(group_mode));
let print_mode =
self.print_group(TagKind::Group, group.mode(), args, queue, stack)?;
if let Some(id) = group.id() {
self.state.group_modes.insert_print_mode(id, group_mode);
self.state.group_modes.insert_print_mode(id, print_mode);
}
}
FormatElement::Tag(StartConditionalGroup(group)) => {
let condition = group.condition();
let expected_mode = match condition.group_id {
None => args.mode(),
Some(id) => self.state.group_modes.unwrap_print_mode(id, element),
};
if expected_mode == condition.mode {
self.print_group(TagKind::ConditionalGroup, group.mode(), args, queue, stack)?;
} else {
// Condition isn't met, render as normal content
stack.push(TagKind::ConditionalGroup, args);
}
}
@ -244,6 +231,29 @@ impl<'a> Printer<'a> {
stack.push(TagKind::Verbatim, args);
}
FormatElement::Tag(StartFitsExpanded(tag::FitsExpanded { condition, .. })) => {
let condition_met = match condition {
Some(condition) => {
let group_mode = match condition.group_id {
Some(group_id) => {
self.state.group_modes.unwrap_print_mode(group_id, element)
}
None => args.mode(),
};
condition.mode == group_mode
}
None => true,
};
if condition_met {
// We measured the inner groups all in expanded. It now is necessary to measure if the inner groups fit as well.
self.state.measured_group_fits = false;
}
stack.push(TagKind::FitsExpanded, args);
}
FormatElement::Tag(tag @ (StartLabelled(_) | StartEntry)) => {
stack.push(tag.kind(), args);
}
@ -252,11 +262,13 @@ impl<'a> Printer<'a> {
tag @ (EndLabelled
| EndEntry
| EndGroup
| EndConditionalGroup
| EndIndent
| EndDedent
| EndAlign
| EndConditionalContent
| EndIndentIfGroupBreaks
| EndFitsExpanded
| EndVerbatim
| EndLineSuffix
| EndFill),
@ -275,6 +287,49 @@ impl<'a> Printer<'a> {
result
}
fn print_group(
&mut self,
kind: TagKind,
mode: GroupMode,
args: PrintElementArgs,
queue: &mut PrintQueue<'a>,
stack: &mut PrintCallStack,
) -> PrintResult<PrintMode> {
let group_mode = match mode {
GroupMode::Expand | GroupMode::Propagated => PrintMode::Expanded,
GroupMode::Flat => {
match args.mode() {
PrintMode::Flat if self.state.measured_group_fits => {
// A parent group has already verified that this group fits on a single line
// Thus, just continue in flat mode
PrintMode::Flat
}
// The printer is either in expanded mode or it's necessary to re-measure if the group fits
// because the printer printed a line break
_ => {
self.state.measured_group_fits = true;
// Measure to see if the group fits up on a single line. If that's the case,
// print the group in "flat" mode, otherwise continue in expanded mode
stack.push(kind, args.with_print_mode(PrintMode::Flat));
let fits = self.fits(queue, stack)?;
stack.pop(kind)?;
if fits {
PrintMode::Flat
} else {
PrintMode::Expanded
}
}
}
}
};
stack.push(kind, args.with_print_mode(group_mode));
Ok(group_mode)
}
fn print_text(&mut self, text: &str, source_range: Option<TextRange>) {
if !self.state.pending_indent.is_empty() {
let (indent_char, repeat_count) = match self.options.indent_style() {
@ -1050,22 +1105,24 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
FormatElement::Tag(StartGroup(group)) => {
if self.must_be_flat && !group.mode().is_flat() {
return Ok(Fits::No);
}
return self.fits_group(TagKind::Group, group.mode(), group.id(), args);
}
// Continue printing groups in expanded mode if measuring a `fits_expanded` element
let print_mode = if !group.mode().is_flat() {
PrintMode::Expanded
} else {
args.mode()
FormatElement::Tag(StartConditionalGroup(group)) => {
let condition = group.condition();
let print_mode = match condition.group_id {
None => args.mode(),
Some(group_id) => self
.group_modes()
.get_print_mode(group_id)
.unwrap_or_else(|| args.mode()),
};
self.stack
.push(TagKind::Group, args.with_print_mode(print_mode));
if let Some(id) = group.id() {
self.group_modes_mut().insert_print_mode(id, print_mode);
if condition.mode == print_mode {
return self.fits_group(TagKind::ConditionalGroup, group.mode(), None, args);
} else {
self.stack.push(TagKind::ConditionalGroup, args);
}
}
@ -1113,6 +1170,42 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
return invalid_end_tag(TagKind::LineSuffix, self.stack.top_kind());
}
FormatElement::Tag(StartFitsExpanded(tag::FitsExpanded {
condition,
propagate_expand,
})) => {
let condition_met = match condition {
Some(condition) => {
let group_mode = match condition.group_id {
Some(group_id) => self
.group_modes()
.get_print_mode(group_id)
.unwrap_or_else(|| args.mode()),
None => args.mode(),
};
condition.mode == group_mode
}
None => true,
};
if condition_met {
// Measure in fully expanded mode.
self.stack.push(
TagKind::FitsExpanded,
args.with_print_mode(PrintMode::Expanded)
.with_measure_mode(MeasureMode::AllLines),
)
} else {
if propagate_expand.get() && args.mode().is_flat() {
return Ok(Fits::No);
}
// As usual
self.stack.push(TagKind::FitsExpanded, args)
}
}
FormatElement::Tag(
tag @ (StartFill | StartVerbatim(_) | StartLabelled(_) | StartEntry),
) => {
@ -1125,11 +1218,13 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
| EndLabelled
| EndEntry
| EndGroup
| EndConditionalGroup
| EndIndentIfGroupBreaks
| EndConditionalContent
| EndAlign
| EndDedent
| EndIndent),
| EndIndent
| EndFitsExpanded),
) => {
self.stack.pop(tag.kind())?;
}
@ -1138,6 +1233,34 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
Ok(Fits::Maybe)
}
fn fits_group(
&mut self,
kind: TagKind,
mode: GroupMode,
id: Option<GroupId>,
args: PrintElementArgs,
) -> PrintResult<Fits> {
if self.must_be_flat && !mode.is_flat() {
return Ok(Fits::No);
}
// Continue printing groups in expanded mode if measuring a `best_fitting` element where
// a group expands.
let print_mode = if !mode.is_flat() {
PrintMode::Expanded
} else {
args.mode()
};
self.stack.push(kind, args.with_print_mode(print_mode));
if let Some(id) = id {
self.group_modes_mut().insert_print_mode(id, print_mode);
}
Ok(Fits::Maybe)
}
fn fits_text(&mut self, text: &str) -> Fits {
let indent = std::mem::take(&mut self.state.pending_indent);
self.state.line_width += indent.level() as usize * self.options().indent_width() as usize

View file

@ -29,10 +29,8 @@ impl Debug for DebugComment<'_> {
strut
.field("text", &self.comment.slice.text(self.source_code))
.field("position", &self.comment.line_position);
#[cfg(debug_assertions)]
strut.field("formatted", &self.comment.formatted.get());
.field("position", &self.comment.line_position)
.field("formatted", &self.comment.formatted.get());
strut.finish()
}