feat(rome_formatter): Dedent to Root IR
MODIFY
crates/rome_formatter/src/builders.rs
crates/rome_formatter/src/builders.rs
@@ -709,12 +709,14 @@ where
{
Dedent {
content: Argument::new(content),
+ mode: DedentMode::Level,
}
}
#[derive(Copy, Clone)]
pub struct Dedent<'a, Context> {
content: Argument<'a, Context>,
+ mode: DedentMode,
}
impl<Context> Format<Context> for Dedent<'_, Context> {
@@ -728,7 +730,10 @@ impl<Context> Format<Context> for Dedent<'_, Context> {
}
let content = buffer.into_vec();
- f.write_element(FormatElement::Dedent(content.into_boxed_slice()))
+ f.write_element(FormatElement::Dedent {
+ content: content.into_boxed_slice(),
+ mode: self.mode,
+ })
}
}
@@ -738,6 +743,56 @@ impl<Context> std::fmt::Debug for Dedent<'_, Context> {
}
}
+/// It resets the indent document so that the content will be printed at the start of the line.
+///
+/// # Examples
+///
+/// ```
+/// use rome_formatter::{format, format_args};
+/// use rome_formatter::prelude::*;
+///
+/// let block = format!(SimpleFormatContext::default(), [
+/// text("root"),
+/// indent(&format_args![
+/// hard_line_break(),
+/// text("indent level 1"),
+/// indent(&format_args![
+/// hard_line_break(),
+/// text("indent level 2"),
+/// align(2, &format_args![
+/// hard_line_break(),
+/// text("two space align"),
+/// dedent_to_root(&format_args![
+/// hard_line_break(),
+/// text("starts at the beginning of the line")
+/// ]),
+/// ]),
+/// hard_line_break(),
+/// text("end indent level 2"),
+/// ])
+/// ]),
+/// ]).unwrap();
+///
+/// assert_eq!(
+/// "root\n\tindent level 1\n\t\tindent level 2\n\t\t two space align\nstarts at the beginning of the line\n\t\tend indent level 2",
+/// block.print().as_code()
+/// );
+/// ```
+///
+/// ## Prettier
+///
+/// This resembles the behaviour of Prettier's `align(Number.NEGATIVE_INFINITY, content)` IR element.
+#[inline]
+pub fn dedent_to_root<Content, Context>(content: &Content) -> Dedent<Context>
+where
+ Content: Format<Context>,
+{
+ Dedent {
+ content: Argument::new(content),
+ mode: DedentMode::Root,
+ }
+}
+
/// Aligns its content by indenting the content by `count` spaces.
///
/// [align] is a variant of `[indent]` that indents its content by a specified number of spaces rather than

MODIFY
crates/rome_formatter/src/format_element.rs
crates/rome_formatter/src/format_element.rs
@@ -40,9 +40,9 @@ pub enum FormatElement {
/// Nesting (Aligns)[FormatElement::Align] has the effect that all except the most inner align are handled as (Indent)[FormatElement::Indent].
Align(Align),
- /// Reduces the indention of the specified content by one. Reverse operation of `Indent` and can be used to
- /// *undo` an `Align` for nested content.
- Dedent(Content),
+ /// Reduces the indention of the specified content either by one level or to the root, depending on the mode.
+ /// Reverse operation of `Indent` and can be used to *undo* an `Align` for nested content.
+ Dedent { content: Content, mode: DedentMode },
/// Creates a logical group where its content is either consistently printed:
/// * on a single line: Omitting `LineMode::Soft` line breaks and printing spaces for `LineMode::SoftOrSpace`
@@ -161,7 +161,11 @@ impl std::fmt::Debug for FormatElement {
FormatElement::Space => write!(fmt, "Space"),
FormatElement::Line(content) => fmt.debug_tuple("Line").field(content).finish(),
FormatElement::Indent(content) => fmt.debug_tuple("Indent").field(content).finish(),
- FormatElement::Dedent(content) => fmt.debug_tuple("Dedent").field(content).finish(),
+ FormatElement::Dedent { content, mode } => fmt
+ .debug_struct("Dedent")
+ .field("content", content)
+ .field("mode", mode)
+ .finish(),
FormatElement::Align(Align { count, content }) => fmt
.debug_struct("Align")
.field("count", count)
@@ -334,6 +338,15 @@ impl PrintMode {
}
}
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum DedentMode {
+ /// Reduces the indent by a level (if the current indent is > 0)
+ Level,
+
+ /// Reduces the indent to the root
+ Root,
+}
+
/// Provides the printer with different representations for the same element so that the printer
/// can pick the best fitting variant.
///
@@ -660,7 +673,7 @@ impl FormatElement {
| FormatElement::Verbatim(Verbatim { content, .. })
| FormatElement::Label(Label { content, .. })
| FormatElement::Indent(content)
- | FormatElement::Dedent(content)
+ | FormatElement::Dedent { content, .. }
| FormatElement::Align(Align { content, .. }) => {
content.iter().any(FormatElement::will_break)
}
@@ -801,8 +814,13 @@ impl Format<IrFormatContext> for FormatElement {
FormatElement::Indent(content) => {
write!(f, [text("indent("), content.as_ref(), text(")")])
}
- FormatElement::Dedent(content) => {
- write!(f, [text("dedent("), content.as_ref(), text(")")])
+ FormatElement::Dedent { content, mode } => {
+ let label = match mode {
+ DedentMode::Level => "dedent",
+ DedentMode::Root => "dedentRoot",
+ };
+
+ write!(f, [text(label), text("("), content.as_ref(), text(")")])
}
FormatElement::Align(Align { content, count }) => {
write!(

MODIFY
crates/rome_formatter/src/printer/mod.rs
crates/rome_formatter/src/printer/mod.rs
@@ -3,7 +3,7 @@ mod printer_options;
pub use printer_options::*;
use crate::format_element::{
- Align, ConditionalGroupContent, Group, LineMode, PrintMode, VerbatimKind,
+ Align, ConditionalGroupContent, DedentMode, Group, LineMode, PrintMode, VerbatimKind,
};
use crate::intersperse::Intersperse;
use crate::{FormatElement, GroupId, IndentStyle, Printed, SourceMarker, TextRange};
@@ -185,8 +185,12 @@ impl<'a> Printer<'a> {
);
}
- FormatElement::Dedent(content) => {
- queue.extend_with_args(content.iter(), args.decrement_indent());
+ FormatElement::Dedent { content, mode } => {
+ let args = match mode {
+ DedentMode::Level => args.decrement_indent(),
+ DedentMode::Root => args.reset_indent(),
+ };
+ queue.extend_with_args(content.iter(), args);
}
FormatElement::Align(Align { content, count }) => {
@@ -581,6 +585,11 @@ impl PrintElementArgs {
self
}
+ pub fn reset_indent(mut self) -> Self {
+ self.indent = Indention::default();
+ self
+ }
+
pub fn set_indent_align(mut self, count: NonZeroU8) -> Self {
self.indent = self.indent.set_align(count);
self
@@ -869,7 +878,13 @@ fn fits_element_on_line<'a, 'rest>(
args.increment_indent_level(options.indent_style()),
),
- FormatElement::Dedent(content) => queue.extend(content.iter(), args.decrement_indent()),
+ FormatElement::Dedent { content, mode } => {
+ let args = match mode {
+ DedentMode::Level => args.decrement_indent(),
+ DedentMode::Root => args.reset_indent(),
+ };
+ queue.extend(content.iter(), args)
+ }
FormatElement::Align(Align { content, count }) => {
queue.extend(content.iter(), args.set_indent_align(*count))

feat(rome_formatter): Dedent to Root IR (#3062)