Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

Commit

Permalink
Merge branch 'main' into feat/parentheses-transform
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Aug 29, 2022
2 parents c4939f9 + 1240c64 commit 21508bc
Show file tree
Hide file tree
Showing 55 changed files with 1,572 additions and 92 deletions.
39 changes: 27 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/rome_console/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license = "MIT"
[dependencies]
rome_markup = { path = "../rome_markup" }
atty = "0.2.14"
text-size = "1.1.0"
rome_text_size = { path = "../rome_text_size"}
lazy_static = "1.4.0"
termcolor = "1.1.2"
similar = "2.1.0"
Expand Down
4 changes: 2 additions & 2 deletions crates/rome_console/src/codespan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::io;

use crate::fmt::{Display, Formatter};
use crate::markup::MarkupBuf;
use text_size::{TextRange, TextSize};
use rome_text_size::{TextRange, TextSize};

use self::render::{MultiLabel, Renderer, SingleLabel};

Expand Down Expand Up @@ -544,7 +544,7 @@ struct Line<'diagnostic> {

#[cfg(test)]
mod tests {
use text_size::{TextRange, TextSize};
use rome_text_size::{TextRange, TextSize};

use crate::codespan::SourceFile;
use crate::{self as rome_console, BufferConsole, ConsoleExt, LogLevel, Markup};
Expand Down
2 changes: 1 addition & 1 deletion crates/rome_console/src/codespan/render.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::io;
use std::{io::Error, ops::Range};

use text_size::{TextRange, TextSize};
use rome_text_size::{TextRange, TextSize};

use crate::fmt::Display;
use crate::markup::MarkupBuf;
Expand Down
2 changes: 1 addition & 1 deletion crates/rome_console/src/markup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::{
io,
};

use rome_text_size::TextSize;
use termcolor::{Color, ColorSpec};
use text_size::TextSize;

use crate::fmt::{Display, Formatter, MarkupElements, Write};

Expand Down
120 changes: 120 additions & 0 deletions crates/rome_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1714,6 +1714,126 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
}
}

/// Increases the indent level by one if the group with the specified id breaks.
///
/// This IR has the same semantics as using [if_group_breaks] and [if_group_fits_on_line] together.
///
/// ```
/// # use rome_formatter::prelude::*;
/// # use rome_formatter::write;
/// # let format = format_with(|f: &mut Formatter<SimpleFormatContext>| {
/// let id = f.group_id("head");
///
/// write!(f, [
/// group(&text("Head")).with_group_id(Some(id)),
/// if_group_breaks(&indent(&text("indented"))).with_group_id(Some(id)),
/// if_group_fits_on_line(&text("indented")).with_group_id(Some(id))
/// ])
///
/// # });
/// ```
///
/// If you want to indent some content if the enclosing group breaks, use [`indent`].
///
/// Use [if_group_breaks] or [if_group_fits_on_line] if the fitting and breaking content differs more than just the
/// indention level.
///
/// # Examples
///
/// Indent the body of an arrow function if the group wrapping the signature breaks:
/// ```
/// use rome_formatter::{format, format_args, LineWidth, SimpleFormatOptions, write};
/// use rome_formatter::prelude::*;
///
/// let content = format_with(|f| {
/// let group_id = f.group_id("header");
///
/// write!(f, [
/// group(&text("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
/// indent_if_group_breaks(&format_args![hard_line_break(), text("a => b")], group_id)
/// ])
/// });
///
/// let context = SimpleFormatContext::new(SimpleFormatOptions {
/// line_width: LineWidth::try_from(20).unwrap(),
/// ..SimpleFormatOptions::default()
/// });
///
/// let formatted = format!(context, [content]).unwrap();
///
/// assert_eq!(
/// "(aLongHeaderThatBreaksForSomeReason) =>\n\ta => b",
/// formatted.print().as_code()
/// );
/// ```
///
/// It doesn't add an indent if the group wrapping the signature doesn't break:
/// ```
/// use rome_formatter::{format, format_args, LineWidth, SimpleFormatOptions, write};
/// use rome_formatter::prelude::*;
///
/// let content = format_with(|f| {
/// let group_id = f.group_id("header");
///
/// write!(f, [
/// group(&text("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
/// indent_if_group_breaks(&format_args![hard_line_break(), text("a => b")], group_id)
/// ])
/// });
///
/// let formatted = format!(SimpleFormatContext::default(), [content]).unwrap();
///
/// assert_eq!(
/// "(aLongHeaderThatBreaksForSomeReason) =>\na => b",
/// formatted.print().as_code()
/// );
/// ```
#[inline]
pub fn indent_if_group_breaks<Content, Context>(
content: &Content,
group_id: GroupId,
) -> IndentIfGroupBreaks<Context>
where
Content: Format<Context>,
{
IndentIfGroupBreaks {
group_id,
content: Argument::new(content),
}
}

#[derive(Copy, Clone)]
pub struct IndentIfGroupBreaks<'a, Context> {
content: Argument<'a, Context>,
group_id: GroupId,
}

impl<Context> Format<Context> for IndentIfGroupBreaks<'_, Context> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
let mut buffer = VecBuffer::new(f.state_mut());

buffer.write_fmt(Arguments::from(&self.content))?;

if buffer.is_empty() {
return Ok(());
}

let content = buffer.into_vec();
f.write_element(FormatElement::IndentIfGroupBreaks(
format_element::IndentIfGroupBreaks::new(content.into_boxed_slice(), self.group_id),
))
}
}

impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IndentIfGroupBreaks")
.field("group_id", &self.group_id)
.field("content", &"{{content}}")
.finish()
}
}

/// Utility for formatting some content with an inline lambda function.
#[derive(Copy, Clone)]
pub struct FormatWith<Context, T> {
Expand Down
59 changes: 43 additions & 16 deletions crates/rome_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rome_rowan::{
#[cfg(debug_assertions)]
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum CommentKind {
Expand Down Expand Up @@ -138,21 +139,26 @@ pub trait CommentStyle<L: Language> {
/// * whether a node should be formatted as is because it has a leading suppression comment.
/// * a node's leading and trailing comments
/// * the dangling comments of a token
///
/// Cloning `comments` is cheap as it only involves bumping a reference counter.
#[derive(Debug, Default, Clone)]
pub struct Comments<L: Language> {
/// Stores the nodes that have at least one leading suppression comment.
suppressed_nodes: HashSet<SyntaxNode<L>>,

/// Stores all nodes for which [Comments::is_suppressed] has been called.
/// This index of nodes that have been checked if they have a suppression comments is used to
/// detect format implementations that manually format a child node without previously checking if
/// the child has a suppression comment.
/// The use of a [Rc] is necessary to achieve that [Comments] has a lifetime that is independent from the [crate::Formatter].
/// Having independent lifetimes is necessary to support the use case where a (formattable object)[crate::Format]
/// iterates over all comments, and writes them into the [crate::Formatter] (mutably borrowing the [crate::Formatter] and in turn its context).
///
/// The implementation refrains from snapshotting the checked nodes because a node gets formatted
/// as verbatim if its formatting fails which has the same result as formatting it as suppressed node
/// (thus, guarantees that the formatting isn't changed).
#[cfg(debug_assertions)]
checked_suppressions: RefCell<HashSet<SyntaxNode<L>>>,
/// ```block
/// for leading in f.context().comments().leading_comments(node) {
/// ^
/// |- Borrows comments
/// write!(f, [comment(leading.piece.text())])?;
/// ^
/// |- Mutably borrows the formatter, state, context, and comments (if comments aren't cloned)
/// }
/// ```
///
/// Using an `Rc` here allows to cheaply clone [Comments] for these use cases.
data: Rc<CommentsData<L>>,
}

impl<L: Language> Comments<L> {
Expand Down Expand Up @@ -202,10 +208,14 @@ impl<L: Language> Comments<L> {
}
}

Self {
let data = CommentsData {
suppressed_nodes,
#[cfg(debug_assertions)]
checked_suppressions: RefCell::default(),
};

Self {
data: Rc::new(data),
}
}

Expand All @@ -225,15 +235,15 @@ impl<L: Language> Comments<L> {
/// call expression is nested inside of the expression statement.
pub fn is_suppressed(&self, node: &SyntaxNode<L>) -> bool {
self.mark_suppression_checked(node);
self.suppressed_nodes.contains(node)
self.data.suppressed_nodes.contains(node)
}

/// Marks that it isn't necessary for the given node to check if it has been suppressed or not.
#[inline]
pub fn mark_suppression_checked(&self, node: &SyntaxNode<L>) {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
let mut checked_nodes = self.checked_suppressions.borrow_mut();
let mut checked_nodes = self.data.checked_suppressions.borrow_mut();
checked_nodes.insert(node.clone());
} else {
let _ = node;
Expand All @@ -250,7 +260,7 @@ impl<L: Language> Comments<L> {
pub(crate) fn assert_checked_all_suppressions(&self, root: &SyntaxNode<L>) {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
let checked_nodes = self.checked_suppressions.borrow();
let checked_nodes = self.data.checked_suppressions.borrow();
for node in root.descendants() {
if node.kind().is_list() || node.kind().is_root() {
continue;
Expand All @@ -274,3 +284,20 @@ Node:
}
}
}

#[derive(Debug, Default)]
struct CommentsData<L: Language> {
/// Stores the nodes that have at least one leading suppression comment.
suppressed_nodes: HashSet<SyntaxNode<L>>,

/// Stores all nodes for which [Comments::is_suppressed] has been called.
/// This index of nodes that have been checked if they have a suppression comments is used to
/// detect format implementations that manually format a child node without previously checking if
/// the child has a suppression comment.
///
/// The implementation refrains from snapshotting the checked nodes because a node gets formatted
/// as verbatim if its formatting fails which has the same result as formatting it as suppressed node
/// (thus, guarantees that the formatting isn't changed).
#[cfg(debug_assertions)]
checked_suppressions: RefCell<HashSet<SyntaxNode<L>>>,
}
Loading

0 comments on commit 21508bc

Please sign in to comment.