mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:09:22 +00:00
perf: Reduce best fitting allocations (#7411)
This commit is contained in:
parent
2421805033
commit
3336d23f48
5 changed files with 173 additions and 83 deletions
|
@ -9,9 +9,7 @@ use Tag::*;
|
||||||
use crate::format_element::tag::{Condition, Tag};
|
use crate::format_element::tag::{Condition, Tag};
|
||||||
use crate::prelude::tag::{DedentMode, GroupMode, LabelId};
|
use crate::prelude::tag::{DedentMode, GroupMode, LabelId};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{
|
use crate::{write, Argument, Arguments, FormatContext, FormatOptions, GroupId, TextSize};
|
||||||
format_element, write, Argument, Arguments, FormatContext, FormatOptions, GroupId, TextSize,
|
|
||||||
};
|
|
||||||
use crate::{Buffer, VecBuffer};
|
use crate::{Buffer, VecBuffer};
|
||||||
|
|
||||||
/// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line.
|
/// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line.
|
||||||
|
@ -2543,15 +2541,12 @@ impl<Context> Format<Context> for BestFitting<'_, Context> {
|
||||||
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
|
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
|
||||||
let variants = self.variants.items();
|
let variants = self.variants.items();
|
||||||
|
|
||||||
let mut formatted_variants = Vec::with_capacity(variants.len());
|
let mut buffer = VecBuffer::with_capacity(variants.len() * 8, f.state_mut());
|
||||||
|
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
let mut buffer = VecBuffer::with_capacity(8, f.state_mut());
|
buffer.write_element(FormatElement::Tag(StartBestFittingEntry));
|
||||||
buffer.write_element(FormatElement::Tag(StartEntry));
|
|
||||||
buffer.write_fmt(Arguments::from(variant))?;
|
buffer.write_fmt(Arguments::from(variant))?;
|
||||||
buffer.write_element(FormatElement::Tag(EndEntry));
|
buffer.write_element(FormatElement::Tag(EndBestFittingEntry));
|
||||||
|
|
||||||
formatted_variants.push(buffer.into_vec().into_boxed_slice());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: The constructor guarantees that there are always at least two variants. It's, therefore,
|
// SAFETY: The constructor guarantees that there are always at least two variants. It's, therefore,
|
||||||
|
@ -2559,9 +2554,7 @@ impl<Context> Format<Context> for BestFitting<'_, Context> {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
let element = unsafe {
|
let element = unsafe {
|
||||||
FormatElement::BestFitting {
|
FormatElement::BestFitting {
|
||||||
variants: format_element::BestFittingVariants::from_vec_unchecked(
|
variants: BestFittingVariants::from_vec_unchecked(buffer.into_vec()),
|
||||||
formatted_variants,
|
|
||||||
),
|
|
||||||
mode: self.mode,
|
mode: self.mode,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub mod tag;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::iter::FusedIterator;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -67,6 +68,16 @@ pub enum FormatElement {
|
||||||
Tag(Tag),
|
Tag(Tag),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FormatElement {
|
||||||
|
pub fn tag_kind(&self) -> Option<TagKind> {
|
||||||
|
if let FormatElement::Tag(tag) = self {
|
||||||
|
Some(tag.kind())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for FormatElement {
|
impl std::fmt::Debug for FormatElement {
|
||||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -318,7 +329,7 @@ pub enum BestFittingMode {
|
||||||
/// The first element is the one that takes up the most space horizontally (the most flat),
|
/// The first element is the one that takes up the most space horizontally (the most flat),
|
||||||
/// The last element takes up the least space horizontally (but most horizontal space).
|
/// The last element takes up the least space horizontally (but most horizontal space).
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub struct BestFittingVariants(Box<[Box<[FormatElement]>]>);
|
pub struct BestFittingVariants(Box<[FormatElement]>);
|
||||||
|
|
||||||
impl BestFittingVariants {
|
impl BestFittingVariants {
|
||||||
/// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
|
/// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
|
||||||
|
@ -331,9 +342,13 @@ impl BestFittingVariants {
|
||||||
/// The slice must contain at least two variants.
|
/// The slice must contain at least two variants.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub unsafe fn from_vec_unchecked(variants: Vec<Box<[FormatElement]>>) -> Self {
|
pub unsafe fn from_vec_unchecked(variants: Vec<FormatElement>) -> Self {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
variants.len() >= 2,
|
variants
|
||||||
|
.iter()
|
||||||
|
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
|
||||||
|
.count()
|
||||||
|
>= 2,
|
||||||
"Requires at least the least expanded and most expanded variants"
|
"Requires at least the least expanded and most expanded variants"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -342,40 +357,85 @@ impl BestFittingVariants {
|
||||||
|
|
||||||
/// Returns the most expanded variant
|
/// Returns the most expanded variant
|
||||||
pub fn most_expanded(&self) -> &[FormatElement] {
|
pub fn most_expanded(&self) -> &[FormatElement] {
|
||||||
self.0.last().expect(
|
self.into_iter().last().unwrap()
|
||||||
"Most contain at least two elements, as guaranteed by the best fitting builder.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[Box<[FormatElement]>] {
|
pub fn as_slice(&self) -> &[FormatElement] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the least expanded variant
|
/// Returns the least expanded variant
|
||||||
pub fn most_flat(&self) -> &[FormatElement] {
|
pub fn most_flat(&self) -> &[FormatElement] {
|
||||||
self.0.first().expect(
|
self.into_iter().next().unwrap()
|
||||||
"Most contain at least two elements, as guaranteed by the best fitting builder.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for BestFittingVariants {
|
impl Deref for BestFittingVariants {
|
||||||
type Target = [Box<[FormatElement]>];
|
type Target = [FormatElement];
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.as_slice()
|
self.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BestFittingVariantsIter<'a> {
|
||||||
|
elements: &'a [FormatElement],
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a BestFittingVariants {
|
impl<'a> IntoIterator for &'a BestFittingVariants {
|
||||||
type Item = &'a Box<[FormatElement]>;
|
type Item = &'a [FormatElement];
|
||||||
type IntoIter = std::slice::Iter<'a, Box<[FormatElement]>>;
|
type IntoIter = BestFittingVariantsIter<'a>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.as_slice().iter()
|
BestFittingVariantsIter { elements: &self.0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for BestFittingVariantsIter<'a> {
|
||||||
|
type Item = &'a [FormatElement];
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.elements.first()? {
|
||||||
|
FormatElement::Tag(Tag::StartBestFittingEntry) => {
|
||||||
|
let end = self
|
||||||
|
.elements
|
||||||
|
.iter()
|
||||||
|
.position(|element| {
|
||||||
|
matches!(element, FormatElement::Tag(Tag::EndBestFittingEntry))
|
||||||
|
})
|
||||||
|
.map_or(self.elements.len(), |position| position + 1);
|
||||||
|
|
||||||
|
let (variant, rest) = self.elements.split_at(end);
|
||||||
|
self.elements = rest;
|
||||||
|
|
||||||
|
Some(variant)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last(mut self) -> Option<Self::Item>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.next_back()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DoubleEndedIterator for BestFittingVariantsIter<'a> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
let start_position = self.elements.iter().rposition(|element| {
|
||||||
|
matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (rest, variant) = self.elements.split_at(start_position);
|
||||||
|
self.elements = rest;
|
||||||
|
Some(variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for BestFittingVariantsIter<'_> {}
|
||||||
|
|
||||||
pub trait FormatElements {
|
pub trait FormatElements {
|
||||||
/// Returns true if this [`FormatElement`] is guaranteed to break across multiple lines by the printer.
|
/// Returns true if this [`FormatElement`] is guaranteed to break across multiple lines by the printer.
|
||||||
/// This is the case if this format element recursively contains a:
|
/// This is the case if this format element recursively contains a:
|
||||||
|
|
|
@ -35,7 +35,10 @@ impl Document {
|
||||||
enum Enclosing<'a> {
|
enum Enclosing<'a> {
|
||||||
Group(&'a tag::Group),
|
Group(&'a tag::Group),
|
||||||
ConditionalGroup(&'a tag::ConditionalGroup),
|
ConditionalGroup(&'a tag::ConditionalGroup),
|
||||||
FitsExpanded(&'a tag::FitsExpanded),
|
FitsExpanded {
|
||||||
|
tag: &'a tag::FitsExpanded,
|
||||||
|
expands_before: bool,
|
||||||
|
},
|
||||||
BestFitting,
|
BestFitting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +46,7 @@ impl Document {
|
||||||
match enclosing.last() {
|
match enclosing.last() {
|
||||||
Some(Enclosing::Group(group)) => group.propagate_expand(),
|
Some(Enclosing::Group(group)) => group.propagate_expand(),
|
||||||
Some(Enclosing::ConditionalGroup(group)) => group.propagate_expand(),
|
Some(Enclosing::ConditionalGroup(group)) => group.propagate_expand(),
|
||||||
Some(Enclosing::FitsExpanded(fits_expanded)) => fits_expanded.propagate_expand(),
|
Some(Enclosing::FitsExpanded { tag, .. }) => tag.propagate_expand(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,23 +88,24 @@ impl Document {
|
||||||
FormatElement::BestFitting { variants, mode: _ } => {
|
FormatElement::BestFitting { variants, mode: _ } => {
|
||||||
enclosing.push(Enclosing::BestFitting);
|
enclosing.push(Enclosing::BestFitting);
|
||||||
|
|
||||||
for variant in variants {
|
propagate_expands(variants, enclosing, checked_interned);
|
||||||
propagate_expands(variant, enclosing, checked_interned);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Best fitting acts as a boundary
|
|
||||||
expands = false;
|
|
||||||
enclosing.pop();
|
enclosing.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FormatElement::Tag(Tag::StartFitsExpanded(fits_expanded)) => {
|
FormatElement::Tag(Tag::StartFitsExpanded(fits_expanded)) => {
|
||||||
enclosing.push(Enclosing::FitsExpanded(fits_expanded));
|
enclosing.push(Enclosing::FitsExpanded {
|
||||||
|
tag: fits_expanded,
|
||||||
|
expands_before: expands,
|
||||||
|
});
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
FormatElement::Tag(Tag::EndFitsExpanded) => {
|
FormatElement::Tag(Tag::EndFitsExpanded) => {
|
||||||
enclosing.pop();
|
if let Some(Enclosing::FitsExpanded { expands_before, .. }) =
|
||||||
// Fits expanded acts as a boundary
|
enclosing.pop()
|
||||||
expands = false;
|
{
|
||||||
|
expands = expands_before;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FormatElement::Text {
|
FormatElement::Text {
|
||||||
|
@ -338,14 +342,20 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatElement::BestFitting { variants, mode } => {
|
FormatElement::BestFitting { variants, mode } => {
|
||||||
write!(f, [token("best_fitting([")])?;
|
write!(f, [token("best_fitting(")])?;
|
||||||
|
|
||||||
|
if *mode != BestFittingMode::FirstLine {
|
||||||
|
write!(f, [text(&std::format!("mode: {mode:?}, "), None)])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, [token("[")])?;
|
||||||
f.write_elements([
|
f.write_elements([
|
||||||
FormatElement::Tag(StartIndent),
|
FormatElement::Tag(StartIndent),
|
||||||
FormatElement::Line(LineMode::Hard),
|
FormatElement::Line(LineMode::Hard),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for variant in variants {
|
for variant in variants {
|
||||||
write!(f, [&**variant, hard_line_break()])?;
|
write!(f, [variant, hard_line_break()])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
f.write_elements([
|
f.write_elements([
|
||||||
|
@ -353,13 +363,7 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
|
||||||
FormatElement::Line(LineMode::Hard),
|
FormatElement::Line(LineMode::Hard),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
write!(f, [token("]")])?;
|
write!(f, [token("])")])?;
|
||||||
|
|
||||||
if *mode != BestFittingMode::FirstLine {
|
|
||||||
write!(f, [text(&std::format!(", mode: {mode:?}"), None),])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, [token(")")])?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatElement::Interned(interned) => {
|
FormatElement::Interned(interned) => {
|
||||||
|
@ -594,10 +598,10 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StartEntry => {
|
StartEntry | StartBestFittingEntry { .. } => {
|
||||||
// handled after the match for all start tags
|
// handled after the match for all start tags
|
||||||
}
|
}
|
||||||
EndEntry => write!(f, [ContentArrayEnd])?,
|
EndEntry | EndBestFittingEntry => write!(f, [ContentArrayEnd])?,
|
||||||
|
|
||||||
EndFill
|
EndFill
|
||||||
| EndLabelled
|
| EndLabelled
|
||||||
|
|
|
@ -83,6 +83,9 @@ pub enum Tag {
|
||||||
|
|
||||||
StartFitsExpanded(FitsExpanded),
|
StartFitsExpanded(FitsExpanded),
|
||||||
EndFitsExpanded,
|
EndFitsExpanded,
|
||||||
|
|
||||||
|
StartBestFittingEntry,
|
||||||
|
EndBestFittingEntry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
|
@ -103,6 +106,7 @@ impl Tag {
|
||||||
| Tag::StartVerbatim(_)
|
| Tag::StartVerbatim(_)
|
||||||
| Tag::StartLabelled(_)
|
| Tag::StartLabelled(_)
|
||||||
| Tag::StartFitsExpanded(_)
|
| Tag::StartFitsExpanded(_)
|
||||||
|
| Tag::StartBestFittingEntry,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +133,7 @@ impl Tag {
|
||||||
StartVerbatim(_) | EndVerbatim => TagKind::Verbatim,
|
StartVerbatim(_) | EndVerbatim => TagKind::Verbatim,
|
||||||
StartLabelled(_) | EndLabelled => TagKind::Labelled,
|
StartLabelled(_) | EndLabelled => TagKind::Labelled,
|
||||||
StartFitsExpanded { .. } | EndFitsExpanded => TagKind::FitsExpanded,
|
StartFitsExpanded { .. } | EndFitsExpanded => TagKind::FitsExpanded,
|
||||||
|
StartBestFittingEntry { .. } | EndBestFittingEntry => TagKind::BestFittingEntry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +157,7 @@ pub enum TagKind {
|
||||||
Verbatim,
|
Verbatim,
|
||||||
Labelled,
|
Labelled,
|
||||||
FitsExpanded,
|
FitsExpanded,
|
||||||
|
BestFittingEntry,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)]
|
||||||
|
|
|
@ -288,7 +288,9 @@ impl<'a> Printer<'a> {
|
||||||
stack.push(TagKind::FitsExpanded, args);
|
stack.push(TagKind::FitsExpanded, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatElement::Tag(tag @ (StartLabelled(_) | StartEntry)) => {
|
FormatElement::Tag(
|
||||||
|
tag @ (StartLabelled(_) | StartEntry | StartBestFittingEntry { .. }),
|
||||||
|
) => {
|
||||||
stack.push(tag.kind(), args);
|
stack.push(tag.kind(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,6 +307,7 @@ impl<'a> Printer<'a> {
|
||||||
| EndFitsExpanded
|
| EndFitsExpanded
|
||||||
| EndVerbatim
|
| EndVerbatim
|
||||||
| EndLineSuffix
|
| EndLineSuffix
|
||||||
|
| EndBestFittingEntry
|
||||||
| EndFill),
|
| EndFill),
|
||||||
) => {
|
) => {
|
||||||
stack.pop(tag.kind())?;
|
stack.pop(tag.kind())?;
|
||||||
|
@ -495,47 +498,64 @@ impl<'a> Printer<'a> {
|
||||||
|
|
||||||
if args.mode().is_flat() && self.state.measured_group_fits {
|
if args.mode().is_flat() && self.state.measured_group_fits {
|
||||||
queue.extend_back(variants.most_flat());
|
queue.extend_back(variants.most_flat());
|
||||||
self.print_entry(queue, stack, args)
|
self.print_entry(queue, stack, args, TagKind::BestFittingEntry)
|
||||||
} else {
|
} else {
|
||||||
self.state.measured_group_fits = true;
|
self.state.measured_group_fits = true;
|
||||||
let normal_variants = &variants[..variants.len() - 1];
|
let mut variants_iter = variants.into_iter();
|
||||||
|
let mut current = variants_iter.next().unwrap();
|
||||||
|
|
||||||
for variant in normal_variants {
|
for next in variants_iter {
|
||||||
// Test if this variant fits and if so, use it. Otherwise try the next
|
// Test if this variant fits and if so, use it. Otherwise try the next
|
||||||
// variant.
|
// variant.
|
||||||
|
|
||||||
// Try to fit only the first variant on a single line
|
// Try to fit only the first variant on a single line
|
||||||
if !matches!(variant.first(), Some(&FormatElement::Tag(Tag::StartEntry))) {
|
if !matches!(
|
||||||
return invalid_start_tag(TagKind::Entry, variant.first());
|
current.first(),
|
||||||
|
Some(&FormatElement::Tag(Tag::StartBestFittingEntry))
|
||||||
|
) {
|
||||||
|
return invalid_start_tag(TagKind::BestFittingEntry, current.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the first element because we want to override the args for the entry and the
|
// Skip the first element because we want to override the args for the entry and the
|
||||||
// args must be popped from the stack as soon as it sees the matching end entry.
|
// args must be popped from the stack as soon as it sees the matching end entry.
|
||||||
let content = &variant[1..];
|
let content = ¤t[1..];
|
||||||
|
|
||||||
let entry_args = args
|
let entry_args = args
|
||||||
.with_print_mode(PrintMode::Flat)
|
.with_print_mode(PrintMode::Flat)
|
||||||
.with_measure_mode(MeasureMode::from(mode));
|
.with_measure_mode(MeasureMode::from(mode));
|
||||||
|
|
||||||
queue.extend_back(content);
|
queue.extend_back(content);
|
||||||
stack.push(TagKind::Entry, entry_args);
|
stack.push(TagKind::BestFittingEntry, entry_args);
|
||||||
let variant_fits = self.fits(queue, stack)?;
|
let variant_fits = self.fits(queue, stack)?;
|
||||||
stack.pop(TagKind::Entry)?;
|
stack.pop(TagKind::BestFittingEntry)?;
|
||||||
|
|
||||||
// Remove the content slice because printing needs the variant WITH the start entry
|
// Remove the content slice because printing needs the variant WITH the start entry
|
||||||
let popped_slice = queue.pop_slice();
|
let popped_slice = queue.pop_slice();
|
||||||
debug_assert_eq!(popped_slice, Some(content));
|
debug_assert_eq!(popped_slice, Some(content));
|
||||||
|
|
||||||
if variant_fits {
|
if variant_fits {
|
||||||
queue.extend_back(variant);
|
queue.extend_back(current);
|
||||||
return self.print_entry(queue, stack, args.with_print_mode(PrintMode::Flat));
|
return self.print_entry(
|
||||||
}
|
queue,
|
||||||
|
stack,
|
||||||
|
args.with_print_mode(PrintMode::Flat),
|
||||||
|
TagKind::BestFittingEntry,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this stage current is the most expanded.
|
||||||
|
|
||||||
// No variant fits, take the last (most expanded) as fallback
|
// No variant fits, take the last (most expanded) as fallback
|
||||||
let most_expanded = variants.most_expanded();
|
queue.extend_back(current);
|
||||||
queue.extend_back(most_expanded);
|
self.print_entry(
|
||||||
self.print_entry(queue, stack, args.with_print_mode(PrintMode::Expanded))
|
queue,
|
||||||
|
stack,
|
||||||
|
args.with_print_mode(PrintMode::Expanded),
|
||||||
|
TagKind::BestFittingEntry,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +706,7 @@ impl<'a> Printer<'a> {
|
||||||
stack: &mut PrintCallStack,
|
stack: &mut PrintCallStack,
|
||||||
args: PrintElementArgs,
|
args: PrintElementArgs,
|
||||||
) -> PrintResult<()> {
|
) -> PrintResult<()> {
|
||||||
self.print_entry(queue, stack, args)
|
self.print_entry(queue, stack, args, TagKind::Entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Semantic alias for [`Self::print_entry`] for fill separators.
|
/// Semantic alias for [`Self::print_entry`] for fill separators.
|
||||||
|
@ -696,7 +716,7 @@ impl<'a> Printer<'a> {
|
||||||
stack: &mut PrintCallStack,
|
stack: &mut PrintCallStack,
|
||||||
args: PrintElementArgs,
|
args: PrintElementArgs,
|
||||||
) -> PrintResult<()> {
|
) -> PrintResult<()> {
|
||||||
self.print_entry(queue, stack, args)
|
self.print_entry(queue, stack, args, TagKind::Entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fully print an element (print the element itself and all its descendants)
|
/// Fully print an element (print the element itself and all its descendants)
|
||||||
|
@ -708,32 +728,31 @@ impl<'a> Printer<'a> {
|
||||||
queue: &mut PrintQueue<'a>,
|
queue: &mut PrintQueue<'a>,
|
||||||
stack: &mut PrintCallStack,
|
stack: &mut PrintCallStack,
|
||||||
args: PrintElementArgs,
|
args: PrintElementArgs,
|
||||||
|
kind: TagKind,
|
||||||
) -> PrintResult<()> {
|
) -> PrintResult<()> {
|
||||||
let start_entry = queue.top();
|
let start_entry = queue.top();
|
||||||
|
|
||||||
if !matches!(start_entry, Some(&FormatElement::Tag(Tag::StartEntry))) {
|
if queue
|
||||||
return invalid_start_tag(TagKind::Entry, start_entry);
|
.pop()
|
||||||
|
.is_some_and(|start| start.tag_kind() == Some(kind))
|
||||||
|
{
|
||||||
|
stack.push(kind, args);
|
||||||
|
} else {
|
||||||
|
return invalid_start_tag(kind, start_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut depth = 0;
|
let mut depth = 1u32;
|
||||||
|
|
||||||
while let Some(element) = queue.pop() {
|
while let Some(element) = queue.pop() {
|
||||||
match element {
|
match element {
|
||||||
FormatElement::Tag(Tag::StartEntry) => {
|
FormatElement::Tag(Tag::StartEntry | Tag::StartBestFittingEntry) => {
|
||||||
// Handle the start of the first element by pushing the args on the stack.
|
|
||||||
if depth == 0 {
|
|
||||||
depth = 1;
|
|
||||||
stack.push(TagKind::Entry, args);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
depth += 1;
|
depth += 1;
|
||||||
}
|
}
|
||||||
FormatElement::Tag(Tag::EndEntry) => {
|
FormatElement::Tag(end_tag @ (Tag::EndEntry | Tag::EndBestFittingEntry)) => {
|
||||||
depth -= 1;
|
depth -= 1;
|
||||||
// Reached the end entry, pop the entry from the stack and return.
|
// Reached the end entry, pop the entry from the stack and return.
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
stack.pop(TagKind::Entry)?;
|
stack.pop(end_tag.kind())?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -745,7 +764,7 @@ impl<'a> Printer<'a> {
|
||||||
self.print_element(stack, queue, element)?;
|
self.print_element(stack, queue, element)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid_end_tag(TagKind::Entry, stack.top_kind())
|
invalid_end_tag(kind, stack.top_kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_char(&mut self, char: char) {
|
fn print_char(&mut self, char: char) {
|
||||||
|
@ -1148,11 +1167,14 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
|
||||||
PrintMode::Expanded => (variants.most_expanded(), args),
|
PrintMode::Expanded => (variants.most_expanded(), args),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(slice.first(), Some(FormatElement::Tag(Tag::StartEntry))) {
|
if !matches!(
|
||||||
return invalid_start_tag(TagKind::Entry, slice.first());
|
slice.first(),
|
||||||
|
Some(FormatElement::Tag(Tag::StartBestFittingEntry))
|
||||||
|
) {
|
||||||
|
return invalid_start_tag(TagKind::BestFittingEntry, slice.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.push(TagKind::Entry, args);
|
self.stack.push(TagKind::BestFittingEntry, args);
|
||||||
self.queue.extend_back(&slice[1..]);
|
self.queue.extend_back(&slice[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1277,7 +1299,11 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatElement::Tag(
|
FormatElement::Tag(
|
||||||
tag @ (StartFill | StartVerbatim(_) | StartLabelled(_) | StartEntry),
|
tag @ (StartFill
|
||||||
|
| StartVerbatim(_)
|
||||||
|
| StartLabelled(_)
|
||||||
|
| StartEntry
|
||||||
|
| StartBestFittingEntry { .. }),
|
||||||
) => {
|
) => {
|
||||||
self.stack.push(tag.kind(), args);
|
self.stack.push(tag.kind(), args);
|
||||||
}
|
}
|
||||||
|
@ -1294,6 +1320,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
|
||||||
| EndAlign
|
| EndAlign
|
||||||
| EndDedent
|
| EndDedent
|
||||||
| EndIndent
|
| EndIndent
|
||||||
|
| EndBestFittingEntry
|
||||||
| EndFitsExpanded),
|
| EndFitsExpanded),
|
||||||
) => {
|
) => {
|
||||||
self.stack.pop(tag.kind())?;
|
self.stack.pop(tag.kind())?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue