From c2eb07c7aafaacc767da4d8e1d0160442795f11f Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 27 Sep 2022 12:37:36 +0200 Subject: [PATCH 1/5] refactor(rome_formatter): Best fitting fits definition --- crates/rome_formatter/src/arguments.rs | 2 +- crates/rome_formatter/src/builders.rs | 99 +++++--- crates/rome_formatter/src/format_element.rs | 86 ++----- .../src/format_element/document.rs | 109 ++++++++- .../rome_formatter/src/format_element/tag.rs | 71 +++++- crates/rome_formatter/src/formatter.rs | 4 +- crates/rome_formatter/src/lib.rs | 47 ++-- crates/rome_formatter/src/macros.rs | 104 ++++++-- crates/rome_formatter/src/prelude.rs | 5 +- crates/rome_formatter/src/printer/mod.rs | 224 +++++++++--------- crates/rome_formatter/src/verbatim.rs | 219 ++++++++++++++++- .../src/js/expressions/array_expression.rs | 14 +- .../js/expressions/template_chunk_element.rs | 10 +- .../src/js/lists/directive_list.rs | 2 +- .../src/jsx/auxiliary/expression_child.rs | 2 +- .../src/jsx/lists/child_list.rs | 10 +- .../src/utils/binary_like_expression.rs | 7 +- crates/rome_js_formatter/src/utils/jsx.rs | 2 +- .../src/utils/object_like.rs | 8 +- .../src/utils/string_utils.rs | 36 ++- 20 files changed, 749 insertions(+), 312 deletions(-) diff --git a/crates/rome_formatter/src/arguments.rs b/crates/rome_formatter/src/arguments.rs index db7d2f46c91..1b954684923 100644 --- a/crates/rome_formatter/src/arguments.rs +++ b/crates/rome_formatter/src/arguments.rs @@ -150,7 +150,7 @@ mod tests { FormatElement::Text(Text::Static { text: "a" }), FormatElement::Space, // Group - FormatElement::Tag(Tag::StartGroup(None)), + FormatElement::Tag(Tag::StartGroup(tag::Group::new())), FormatElement::Text(Text::Static { text: "(" }), FormatElement::Text(Text::Static { text: ")" }), FormatElement::Tag(Tag::EndGroup) diff --git a/crates/rome_formatter/src/builders.rs b/crates/rome_formatter/src/builders.rs index 6ad608a97ae..2331318b18f 100644 --- a/crates/rome_formatter/src/builders.rs +++ b/crates/rome_formatter/src/builders.rs @@ -1,5 +1,5 @@ use crate::format_element::tag::{Condition, Tag}; -use crate::prelude::tag::{DedentMode, LabelId}; +use crate::prelude::tag::{DedentMode, GroupMode, LabelId}; use crate::prelude::*; use crate::{format_element, write, Argument, Arguments, GroupId, TextRange, TextSize}; use crate::{Buffer, VecBuffer}; @@ -100,6 +100,38 @@ pub const fn hard_line_break() -> Line { Line::new(LineMode::Hard) } +/// A plain line break that does not add any indent. +/// Useful for line breaks in a spacing sensitive context. +/// +/// # Examples +/// +/// ``` +/// use rome_formatter::{format, format_args}; +/// use rome_formatter::prelude::*; +/// +/// # fn main() -> FormatResult<()> { +/// let block = format!(SimpleFormatContext::default(), [ +/// text("a ="), +/// block_indent(&format_args![ +/// text("`abcd"), +/// literal_line(), +/// text("...no indent at the beginning`") +/// ]), +/// text(";"), +/// ])?; +/// +/// assert_eq!( +/// "a =\n\t`abcd\n...no indent at the beginning`\n;", +/// block.print()?.as_code() +/// ); +/// # Ok(()) +/// # } +/// ``` +#[inline] +pub const fn literal_line() -> Line { + Line::new(LineMode::Literal) +} + /// A forced empty line. An empty line inserts enough line breaks in the output for /// the previous and next element to be separated by an empty line. /// @@ -1332,14 +1364,12 @@ impl Group<'_, Context> { self } - /// Setting the value to `true` forces the group and its enclosing group to expand regardless if it otherwise would fit on the - /// line or contains any hard line breaks. + /// Changes the [PrintMode] of the group from [`Flat`](PrintMode::Flat) to [`Expanded`](PrintMode::Expanded). + /// The result is that any soft-line break gets printed as a regular line break. /// - /// The formatter writes a [FormatElement::ExpandParent], forcing any enclosing group to expand, if `should_expand` is `true`. - /// It also omits the [`start`](Tag::StartGroup) and [`end`](Tag::EndGroup) tags because the group would be forced to expand anyway. - /// The [`start`](Tag::StartGroup) and [`end`](Tag::EndGroup) tags are only written if the `group_id` specified with [Group::with_group_id] isn't [None] - /// because other IR elements may reference the group with that group id and the printer may panic - /// if no group with the given id is present in the document. + /// This is useful for content rendered inside of a [FormatElement::BestFitting] that prints each variant + /// in [PrintMode::Flat] to change some content to be printed in [`Expanded`](PrintMode::Expanded) regardless. + /// See the documentation of the [`best_fitting`] macro for an example. pub fn should_expand(mut self, should_expand: bool) -> Self { self.should_expand = should_expand; self @@ -1348,28 +1378,18 @@ impl Group<'_, Context> { impl Format for Group<'_, Context> { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - if self.group_id.is_none() && self.should_expand { - write!(f, [expand_parent()])?; - return f.write_fmt(Arguments::from(&self.content)); - } - - let write_group = !self.should_expand || self.group_id.is_some(); - - if write_group { - f.write_element(FormatElement::Tag(Tag::StartGroup(self.group_id)))?; - } + let mode = match self.should_expand { + true => GroupMode::Expand, + false => GroupMode::Flat, + }; - if self.should_expand { - write!(f, [expand_parent()])?; - } + f.write_element(FormatElement::Tag(StartGroup( + tag::Group::new().with_id(self.group_id).with_mode(mode), + )))?; Arguments::from(&self.content).fmt(f)?; - if write_group { - f.write_element(FormatElement::Tag(Tag::EndGroup))?; - } - - Ok(()) + f.write_element(FormatElement::Tag(EndGroup)) } } @@ -1438,7 +1458,7 @@ impl Format for ExpandParent { /// /// The element has no special meaning if used outside of a `Group`. In that case, the content is always emitted. /// -/// If you're looking for a way to only print something if the `Group` fits on a single line see [crate::if_group_fits_on_line]. +/// If you're looking for a way to only print something if the `Group` fits on a single line see [self::if_group_fits_on_line]. /// /// # Examples /// @@ -2119,21 +2139,26 @@ where /// Get the number of line breaks between two consecutive SyntaxNodes in the tree pub fn get_lines_before(next_node: &SyntaxNode) -> usize { // Count the newlines in the leading trivia of the next node - if let Some(leading_trivia) = next_node.first_leading_trivia() { - leading_trivia - .pieces() - .take_while(|piece| { - // Stop at the first comment or skipped piece, the comment printer - // will handle newlines between the comment and the node - !(piece.is_comments() || piece.is_skipped()) - }) - .filter(|piece| piece.is_newline()) - .count() + if let Some(token) = next_node.first_token() { + get_lines_before_token(&token) } else { 0 } } +pub fn get_lines_before_token(token: &SyntaxToken) -> usize { + token + .leading_trivia() + .pieces() + .take_while(|piece| { + // Stop at the first comment or skipped piece, the comment printer + // will handle newlines between the comment and the node + !(piece.is_comments() || piece.is_skipped()) + }) + .filter(|piece| piece.is_newline()) + .count() +} + /// Builder to fill as many elements as possible on a single line. #[must_use = "must eventually call `finish()` on Format builders"] pub struct FillBuilder<'fmt, 'buf, Context> { diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs index ae2523c7bcb..e12e4db0b28 100644 --- a/crates/rome_formatter/src/format_element.rs +++ b/crates/rome_formatter/src/format_element.rs @@ -7,26 +7,25 @@ use crate::{TagKind, TextSize}; #[cfg(target_pointer_width = "64")] use rome_rowan::static_assert; use rome_rowan::SyntaxTokenText; -use std::borrow::Cow; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::rc::Rc; /// Language agnostic IR for formatting source code. /// -/// Use the helper functions like [crate::space], [crate::soft_line_break] etc. defined in this file to create elements. +/// Use the helper functions like [crate::builders::space], [crate::builders::soft_line_break] etc. defined in this file to create elements. #[derive(Clone, Eq, PartialEq)] pub enum FormatElement { - /// A space token, see [crate::space] for documentation. + /// A space token, see [crate::builders::space] for documentation. Space, - /// A new line, see [crate::soft_line_break], [crate::hard_line_break], and [crate::soft_line_break_or_space] for documentation. + /// A new line, see [crate::builders::soft_line_break], [crate::builders::hard_line_break], and [crate::builders::soft_line_break_or_space] for documentation. Line(LineMode), /// Forces the parent group to print in expanded mode. ExpandParent, - /// A text that should be printed as is, see [crate::text] for documentation and examples. + /// A text that should be printed as is, see [crate::builders::text] for documentation and examples. Text(Text), /// Prevents that line suffixes move past this boundary. Forces the printer to print any pending @@ -66,13 +65,15 @@ impl std::fmt::Debug for FormatElement { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum LineMode { - /// See [crate::soft_line_break_or_space] for documentation. + /// See [crate::builders::soft_line_break_or_space] for documentation. SoftOrSpace, - /// See [crate::soft_line_break] for documentation. + /// See [crate::builders::soft_line_break] for documentation. Soft, - /// See [crate::hard_line_break] for documentation. + /// See [crate::builders::hard_line_break] for documentation. Hard, - /// See [crate::empty_line] for documentation. + /// See [crate::builders::literal_line] for documentation. + Literal, + /// See [crate::builders::empty_line] for documentation. Empty, } @@ -80,6 +81,10 @@ impl LineMode { pub const fn is_hard(&self) -> bool { matches!(self, LineMode::Hard) } + + pub const fn is_literal(&self) -> bool { + matches!(self, LineMode::Literal) + } } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -140,7 +145,7 @@ impl Deref for Interned { } } -/// See [crate::text] for documentation +/// See [crate::builders::text] for documentation #[derive(Eq, Clone)] pub enum Text { /// Token constructed by the formatter from a static string @@ -202,38 +207,6 @@ impl PartialEq for Text { } } -const LINE_SEPARATOR: char = '\u{2028}'; -const PARAGRAPH_SEPARATOR: char = '\u{2029}'; -pub const LINE_TERMINATORS: [char; 3] = ['\r', LINE_SEPARATOR, PARAGRAPH_SEPARATOR]; - -/// Replace the line terminators matching the provided list with "\n" -/// since its the only line break type supported by the printer -pub fn normalize_newlines(text: &str, terminators: [char; N]) -> Cow { - let mut result = String::new(); - let mut last_end = 0; - - for (start, part) in text.match_indices(terminators) { - result.push_str(&text[last_end..start]); - result.push('\n'); - - last_end = start + part.len(); - // If the current character is \r and the - // next is \n, skip over the entire sequence - if part == "\r" && text[last_end..].starts_with('\n') { - last_end += 1; - } - } - - // If the result is empty no line terminators were matched, - // return the entire input text without allocating a new String - if result.is_empty() { - Cow::Borrowed(text) - } else { - result.push_str(&text[last_end..text.len()]); - Cow::Owned(result) - } -} - impl Deref for Text { type Target = str; fn deref(&self) -> &Self::Target { @@ -274,13 +247,19 @@ impl FormatElements for FormatElement { fn will_break(&self) -> bool { match self { FormatElement::ExpandParent => true, - FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty), - FormatElement::Text(text) => text.contains('\n'), + FormatElement::Tag(Tag::StartGroup(group)) => !group.mode().is_flat(), + FormatElement::Line(line_mode) => matches!( + line_mode, + LineMode::Hard | LineMode::Empty | LineMode::Literal + ), FormatElement::Interned(interned) => interned.will_break(), + // Traverse into the most flat version because the content is guaranteed to expand when even + // the most flat version contains some content that forces a break. + FormatElement::BestFitting(best_fitting) => best_fitting.most_flat().will_break(), FormatElement::LineSuffixBoundary | FormatElement::Space | FormatElement::Tag(_) - | FormatElement::BestFitting(_) => false, + | FormatElement::Text(_) => false, } } @@ -371,7 +350,7 @@ impl std::fmt::Debug for BestFitting { pub trait FormatElements { /// 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: - /// * [crate::empty_line] or [crate::hard_line_break] + /// * [crate::builders::empty_line] or [crate::builders::hard_line_break] /// * A token containing '\n' /// /// Use this with caution, this is only a heuristic and the printer may print the element over multiple @@ -391,21 +370,6 @@ pub trait FormatElements { fn end_tag(&self, kind: TagKind) -> Option<&Tag>; } -#[cfg(test)] -mod tests { - - use crate::format_element::{normalize_newlines, LINE_TERMINATORS}; - - #[test] - fn test_normalize_newlines() { - assert_eq!(normalize_newlines("a\nb", LINE_TERMINATORS), "a\nb"); - assert_eq!(normalize_newlines("a\rb", LINE_TERMINATORS), "a\nb"); - assert_eq!(normalize_newlines("a\r\nb", LINE_TERMINATORS), "a\nb"); - assert_eq!(normalize_newlines("a\u{2028}b", LINE_TERMINATORS), "a\nb"); - assert_eq!(normalize_newlines("a\u{2029}b", LINE_TERMINATORS), "a\nb"); - } -} - #[cfg(target_pointer_width = "64")] static_assert!(std::mem::size_of::() == 8usize); diff --git a/crates/rome_formatter/src/format_element/document.rs b/crates/rome_formatter/src/format_element/document.rs index 4fa41649ff7..771bb9264b5 100644 --- a/crates/rome_formatter/src/format_element/document.rs +++ b/crates/rome_formatter/src/format_element/document.rs @@ -1,5 +1,6 @@ use super::tag::Tag; use crate::format_element::tag::DedentMode; +use crate::prelude::tag::GroupMode; use crate::prelude::*; use crate::printer::LineEnding; use crate::{format, write}; @@ -8,6 +9,7 @@ use crate::{ IndentStyle, LineWidth, PrinterOptions, TransformSourceMap, }; use rome_rowan::TextSize; +use rustc_hash::FxHashMap; use std::collections::HashMap; use std::ops::Deref; @@ -17,6 +19,88 @@ pub struct Document { elements: Vec, } +impl Document { + /// Sets [`expand`](tag::Group::expand) to [`GroupMode::Propagated`] if the group contains any of: + /// * a group with [`expand`](tag::Group::expand) set to [GroupMode::Propagated] or [GroupMode::Expand]. + /// * a non-soft [line break](FormatElement::Line) with mode [LineMode::Hard], [LineMode::Empty], or [LineMode::Literal]. + /// * a [FormatElement::ExpandParent] + /// + /// [`BestFitting`] elements act as expand boundaries, meaning that the fact that a + /// [`BestFitting`]'s content expands is not propagated past the [`BestFitting`] element. + /// + /// [`BestFitting`](FormatElement::BestFitting) + pub(crate) fn propagate_expand(&mut self) { + #[derive(Debug)] + enum Enclosing<'a> { + Group(&'a tag::Group), + BestFitting, + } + + fn expand_parent(enclosing: &[Enclosing]) { + if let Some(Enclosing::Group(group)) = enclosing.last() { + group.propagate_expand(); + } + } + + fn propagate_expands<'a>( + elements: &'a [FormatElement], + enclosing: &mut Vec>, + checked_interned: &mut FxHashMap<&'a Interned, bool>, + ) -> bool { + let mut expands = false; + for element in elements { + let element_expands = match element { + FormatElement::Tag(Tag::StartGroup(group)) => { + enclosing.push(Enclosing::Group(group)); + false + } + FormatElement::Tag(Tag::EndGroup) => match enclosing.pop() { + Some(Enclosing::Group(group)) => !group.mode().is_flat(), + _ => false, + }, + FormatElement::Interned(interned) => match checked_interned.get(interned) { + Some(interned_expands) => *interned_expands, + None => { + let interned_expands = + propagate_expands(interned, enclosing, checked_interned); + checked_interned.insert(interned, interned_expands); + interned_expands + } + }, + FormatElement::BestFitting(best_fitting) => { + enclosing.push(Enclosing::BestFitting); + + for variant in best_fitting.variants() { + propagate_expands(variant, enclosing, checked_interned); + } + + // Best fitting acts as a boundary + expands = false; + enclosing.pop(); + continue; + } + FormatElement::ExpandParent + | FormatElement::Line(LineMode::Hard | LineMode::Empty | LineMode::Literal) => { + true + } + _ => false, + }; + + if element_expands { + expands = true; + expand_parent(enclosing) + } + } + + expands + } + + let mut enclosing: Vec = Vec::new(); + let mut interned: FxHashMap<&Interned, bool> = FxHashMap::default(); + propagate_expands(self, &mut enclosing, &mut interned); + } +} + impl From> for Document { fn from(elements: Vec) -> Self { Self { elements } @@ -145,6 +229,9 @@ impl Format for &[FormatElement] { LineMode::Empty => { write!(f, [text("empty_line")])?; } + LineMode::Literal => { + write!(f, [text("literal_line")])?; + } }, FormatElement::ExpandParent => { write!(f, [text("expand_parent")])?; @@ -292,10 +379,10 @@ impl Format for &[FormatElement] { write!(f, [text("verbatim(")])?; } - StartGroup(id) => { + StartGroup(group) => { write!(f, [text("group(")])?; - if let Some(group_id) = id { + if let Some(group_id) = group.id() { write!( f, [ @@ -308,6 +395,16 @@ impl Format for &[FormatElement] { ] )?; } + + match group.mode() { + GroupMode::Flat => {} + GroupMode::Expand => { + write!(f, [text("expand: true,"), space()])?; + } + GroupMode::Propagated => { + write!(f, [text("expand: propagated,"), space()])?; + } + } } StartIndentIfGroupBreaks(id) => { @@ -351,12 +448,12 @@ impl Format for &[FormatElement] { write!( f, [ - text("label(\""), + text("label("), dynamic_text( &std::format!("\"{label_id:?}\""), TextSize::default() ), - text("\","), + text(","), space(), ] )?; @@ -420,7 +517,7 @@ impl Format for ContentArrayStart { write!(f, [text("[")])?; f.write_elements([ - FormatElement::Tag(StartGroup(None)), + FormatElement::Tag(StartGroup(tag::Group::new())), FormatElement::Tag(StartIndent), FormatElement::Line(LineMode::Soft), ]) @@ -585,7 +682,7 @@ mod tests { let document = Document::from(vec![ FormatElement::Text(Text::Static { text: "[" }), - FormatElement::Tag(StartGroup(None)), + FormatElement::Tag(StartGroup(tag::Group::new())), FormatElement::Tag(StartIndent), FormatElement::Line(LineMode::Soft), FormatElement::Text(Text::Static { text: "a" }), diff --git a/crates/rome_formatter/src/format_element/tag.rs b/crates/rome_formatter/src/format_element/tag.rs index c546152cc8e..adc3c96ac2f 100644 --- a/crates/rome_formatter/src/format_element/tag.rs +++ b/crates/rome_formatter/src/format_element/tag.rs @@ -3,6 +3,7 @@ use crate::{GroupId, TextSize}; #[cfg(debug_assertions)] use std::any::type_name; use std::any::TypeId; +use std::cell::Cell; use std::num::NonZeroU8; /// A Tag marking the start and end of some content to which some special formatting should be applied. @@ -11,7 +12,7 @@ use std::num::NonZeroU8; /// will be applied to all elements in between the start/end tags. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Tag { - /// Indents the content one level deeper, see [crate::indent] for documentation and examples. + /// Indents the content one level deeper, see [crate::builders::indent] for documentation and examples. StartIndent, EndIndent, @@ -31,12 +32,12 @@ pub enum Tag { /// * on a single line: Omitting `LineMode::Soft` line breaks and printing spaces for `LineMode::SoftOrSpace` /// * on multiple lines: Printing all line breaks /// - /// See [crate::group] for documentation and examples. - StartGroup(Option), + /// See [crate::builders::group] for documentation and examples. + StartGroup(Group), EndGroup, /// Allows to specify content that gets printed depending on whatever the enclosing group - /// is printed on a single line or multiple lines. See [crate::if_group_breaks] for examples. + /// is printed on a single line or multiple lines. See [crate::builders::if_group_breaks] for examples. StartConditionalContent(Condition), EndConditionalContent, @@ -66,7 +67,7 @@ pub enum Tag { /// Special semantic element marking the content with a label. /// This does not directly influence how the content will be printed. /// - /// See [crate::labelled] for documentation. + /// See [crate::builders::labelled] for documentation. StartLabelled(LabelId), EndLabelled, } @@ -79,7 +80,7 @@ impl Tag { Tag::StartIndent | Tag::StartAlign(_) | Tag::StartDedent(_) - | Tag::StartGroup(_) + | Tag::StartGroup { .. } | Tag::StartConditionalContent(_) | Tag::StartIndentIfGroupBreaks(_) | Tag::StartFill @@ -133,6 +134,64 @@ pub enum TagKind { Labelled, } +#[derive(Debug, Copy, Default, Clone, Eq, PartialEq)] +pub enum GroupMode { + /// Print group in flat mode. + #[default] + Flat, + + /// The group should be printed in expanded mode + Expand, + + /// Expand mode has been propagated from an enclosing group to this group. + Propagated, +} + +impl GroupMode { + pub const fn is_flat(&self) -> bool { + matches!(self, GroupMode::Flat) + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct Group { + id: Option, + mode: Cell, +} + +impl Group { + pub fn new() -> Self { + Self { + id: None, + mode: Cell::new(GroupMode::Flat), + } + } + + pub fn with_id(mut self, id: Option) -> Self { + self.id = id; + self + } + + pub fn with_mode(mut self, mode: GroupMode) -> Self { + self.mode = Cell::new(mode); + self + } + + pub fn mode(&self) -> GroupMode { + self.mode.get() + } + + pub fn propagate_expand(&self) { + if self.mode.get() == GroupMode::Flat { + self.mode.set(GroupMode::Propagated) + } + } + + pub fn id(&self) -> Option { + self.id + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum DedentMode { /// Reduces the indent by a level (if the current indent is > 0) diff --git a/crates/rome_formatter/src/formatter.rs b/crates/rome_formatter/src/formatter.rs index ec6a8f1db3a..17db4b5c51e 100644 --- a/crates/rome_formatter/src/formatter.rs +++ b/crates/rome_formatter/src/formatter.rs @@ -115,7 +115,7 @@ impl<'buf, Context> Formatter<'buf, Context> { /// line break or empty line depending on the input file. /// /// This functions inspects the input source and separates consecutive elements with either - /// a [crate::soft_line_break_or_space] or [crate::empty_line] depending on how many line breaks were + /// a [crate::builders::soft_line_break_or_space] or [crate::builders::empty_line] depending on how many line breaks were /// separating the elements in the original file. pub fn join_nodes_with_soft_line<'a>( &'a mut self, @@ -127,7 +127,7 @@ impl<'buf, Context> Formatter<'buf, Context> { /// line breaks depending on the input file. /// /// This functions inspects the input source and separates consecutive elements with either - /// a [crate::hard_line_break] or [crate::empty_line] depending on how many line breaks were separating the + /// a [crate::builders::hard_line_break] or [crate::builders::empty_line] depending on how many line breaks were separating the /// elements in the original file. pub fn join_nodes_with_hardline<'a>(&'a mut self) -> JoinNodesBuilder<'a, 'buf, Line, Context> { JoinNodesBuilder::new(hard_line_break(), self) diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs index cccb1794d2a..e4cec89773d 100644 --- a/crates/rome_formatter/src/lib.rs +++ b/crates/rome_formatter/src/lib.rs @@ -40,7 +40,7 @@ mod verbatim; use crate::formatter::Formatter; use crate::group_id::UniqueGroupIdBuilder; -use crate::prelude::{syntax_token_cow_slice, TagKind}; +use crate::prelude::TagKind; use crate::format_element::document::Document; #[cfg(debug_assertions)] @@ -48,18 +48,15 @@ use crate::printed_tokens::PrintedTokens; use crate::printer::{Printer, PrinterOptions}; pub use arguments::{Argument, Arguments}; pub use buffer::{Buffer, BufferExtensions, BufferSnapshot, Inspect, PreambleBuffer, VecBuffer}; -pub use builders::{ - block_indent, empty_line, get_lines_before, group, hard_line_break, if_group_breaks, - if_group_fits_on_line, indent, labelled, line_suffix, soft_block_indent, soft_line_break, - soft_line_break_or_space, soft_line_indent_or_space, space, text, BestFitting, -}; +pub use builders::BestFitting; use crate::comments::{CommentStyle, Comments, SourceComment}; -pub use format_element::{normalize_newlines, FormatElement, Text, LINE_TERMINATORS}; +use crate::verbatim::{normalize_token_text_new_lines, LINE_TERMINATORS}; +pub use format_element::{FormatElement, Text}; pub use group_id::GroupId; use rome_rowan::{ Language, SyntaxElement, SyntaxError, SyntaxNode, SyntaxResult, SyntaxToken, SyntaxTriviaPiece, - TextRange, TextSize, TokenAtOffset, + TextLen, TextRange, TextSize, TokenAtOffset, }; pub use source_map::{TransformSourceMap, TransformSourceMapBuilder}; use std::error::Error; @@ -960,10 +957,10 @@ where buffer.write_fmt(arguments)?; - Ok(Formatted::new( - Document::from(buffer.into_vec()), - state.into_context(), - )) + let mut document = Document::from(buffer.into_vec()); + document.propagate_expand(); + + Ok(Formatted::new(document, state.into_context())) } /// Entry point for formatting a [SyntaxNode] for a specific language. @@ -1032,17 +1029,18 @@ pub fn format_node( write!(buffer, [format_node])?; - let document = Document::from(buffer.into_vec()); + let mut document = Document::from(buffer.into_vec()); + document.propagate_expand(); state.assert_formatted_all_tokens(&root); - { - let comments = state.context().comments(); - comments.assert_checked_all_suppressions(&root); - comments.assert_formatted_all_comments(); - } + let context = state.into_context(); + let comments = context.comments(); + + comments.assert_checked_all_suppressions(&root); + comments.assert_formatted_all_comments(); - Ok(Formatted::new(document, state.into_context())) + Ok(Formatted::new(document, context)) }) } @@ -1395,12 +1393,17 @@ impl Format for SyntaxTriviaPiece { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { let range = self.text_range(); + // Trim start/end and update the range + let trimmed = self.text().trim_start(); + let trimmed_start = range.start() + (range.len() - trimmed.text_len()); + let trimmed = trimmed.trim_end(); + write!( f, - [syntax_token_cow_slice( - normalize_newlines(self.text().trim(), LINE_TERMINATORS), + [normalize_token_text_new_lines( &self.token(), - range.start() + TextRange::at(trimmed_start, trimmed.text_len()), + LINE_TERMINATORS )] ) } diff --git a/crates/rome_formatter/src/macros.rs b/crates/rome_formatter/src/macros.rs index f374199b01a..0d2f9a3b0b4 100644 --- a/crates/rome_formatter/src/macros.rs +++ b/crates/rome_formatter/src/macros.rs @@ -238,6 +238,72 @@ macro_rules! format { /// # } /// ``` /// +/// ### Enclosing group with `should_expand: true` +/// +/// ``` +/// use rome_formatter::{Formatted, LineWidth, format, format_args, SimpleFormatOptions}; +/// use rome_formatter::prelude::*; +/// +/// # fn main() -> FormatResult<()> { +/// let formatted = format!( +/// SimpleFormatContext::default(), +/// [ +/// best_fitting!( +/// // Prints the method call on the line but breaks the array. +/// format_args!( +/// text("expect(a).toMatch("), +/// group(&format_args![ +/// text("["), +/// soft_block_indent(&format_args![ +/// text("1,"), +/// soft_line_break_or_space(), +/// text("2,"), +/// soft_line_break_or_space(), +/// text("3"), +/// ]), +/// text("]") +/// ]).should_expand(true), +/// text(")") +/// ), +/// +/// // Breaks after `(` +/// format_args!( +/// text("expect(a).toMatch("), +/// group(&soft_block_indent( +/// &group(&format_args![ +/// text("["), +/// soft_block_indent(&format_args![ +/// text("1,"), +/// soft_line_break_or_space(), +/// text("2,"), +/// soft_line_break_or_space(), +/// text("3"), +/// ]), +/// text("]") +/// ]).should_expand(true), +/// )).should_expand(true), +/// text(")") +/// ), +/// ) +/// ] +/// )?; +/// +/// let document = formatted.into_document(); +/// +/// assert_eq!( +/// "expect(a).toMatch([\n\t1,\n\t2,\n\t3\n])", +/// Formatted::new(document.clone(), SimpleFormatContext::default()) +/// .print()? +/// .as_code() +/// ); +/// +/// # Ok(()) +/// # } +/// ``` +/// +/// The first variant fits because all its content up to the first line break fit on the line without exceeding +/// the configured print width. +/// /// ## Complexity /// Be mindful of using this IR element as it has a considerable performance penalty: /// * There are multiple representation for the same content. This results in increased memory usage @@ -246,19 +312,20 @@ macro_rules! format { /// complexity if used in nested structures. /// /// ## Behavior -/// This IR is similar to Prettier's `conditionalGroup`. It provides similar functionality but -/// differs in that Prettier automatically wraps each variant in a `Group`. Rome doesn't do so. -/// You can wrap the variant content in a group if you want to use soft line breaks. -/// Unlike in Prettier, the printer will try to fit **only the first variant** in [`Flat`] mode, -/// then it will try to fit the rest of the variants in [`Expanded`] mode. +/// This IR is similar to Prettier's `conditionalGroup`. The printer measures each variant, except the [`MostExpanded`], in [`Flat`] mode +/// to find the first variant that fits and prints this variant in [`Flat`] mode. If no variant fits, then +/// the printer falls back to printing the [`MostExpanded`] variant in `[`Expanded`] mode. +/// +/// The definition of *fits* differs to groups in that the printer only tests if it is possible to print +/// the content up to the first non-soft line break without exceeding the configured print width. +/// This definition differs from groups as that non-soft line breaks make group expand. +/// +/// [crate::BestFitting] acts as a "break" boundary, meaning that it is considered to fit /// -/// A variant that is measured in [`Expanded`] mode will be considered to fit if it can be printed without -/// overflowing the current line with all of its inner groups expanded. Those inner groups could still end -/// up being printed in flat mode if they fit on the line while printing. But there is currently no way -/// to enforce that a specific group inside a variant must be flat when measuring if that variant fits. /// /// [`Flat`]: crate::format_element::PrintMode::Flat /// [`Expanded`]: crate::format_element::PrintMode::Expanded +/// [`MostExpanded`]: crate::format_element::BestFitting::most_expanded #[macro_export] macro_rules! best_fitting { ($least_expanded:expr, $($tail:expr),+ $(,)?) => {{ @@ -328,14 +395,14 @@ mod tests { [ text("aVeryLongIdentifier"), soft_line_break_or_space(), - best_fitting!( - format_args!(text( - "Something that will not fit on a 30 character print width." - ),), - format_args![ + best_fitting![ + format_args![text( + "Something that will not fit on a line with 30 character print width." + )], + format_args![group(&format_args![ text("Start"), soft_line_break(), - &group(&soft_block_indent(&format_args![ + group(&soft_block_indent(&format_args![ text("1,"), soft_line_break_or_space(), text("2,"), @@ -343,7 +410,7 @@ mod tests { text("3"), ])), soft_line_break_or_space(), - &soft_block_indent(&format_args![ + soft_block_indent(&format_args![ text("1,"), soft_line_break_or_space(), text("2,"), @@ -358,9 +425,10 @@ mod tests { ]), soft_line_break_or_space(), text("End") - ], + ]) + .should_expand(true)], format_args!(text("Most"), hard_line_break(), text("Expanded")) - ) + ] ] ) .unwrap(); diff --git a/crates/rome_formatter/src/prelude.rs b/crates/rome_formatter/src/prelude.rs index 1a3a276a991..5870b9c0f51 100644 --- a/crates/rome_formatter/src/prelude.rs +++ b/crates/rome_formatter/src/prelude.rs @@ -8,7 +8,10 @@ pub use crate::trivia::{ format_replaced, format_trailing_comments, format_trimmed_token, }; -pub use crate::verbatim::{format_suppressed_node, format_unknown_node, format_verbatim_node}; +pub use crate::verbatim::{ + format_suppressed_node, format_unknown_node, format_verbatim_node, normalize_newlines, + normalize_token_text_new_lines, LINE_TERMINATORS, +}; pub use crate::format_element::document::Document; pub use crate::format_element::tag::{LabelId, Tag, TagKind}; diff --git a/crates/rome_formatter/src/printer/mod.rs b/crates/rome_formatter/src/printer/mod.rs index db94982a4fc..177b6772719 100644 --- a/crates/rome_formatter/src/printer/mod.rs +++ b/crates/rome_formatter/src/printer/mod.rs @@ -162,32 +162,35 @@ impl<'a> Printer<'a> { } else if self.state.line_suffixes.has_pending() { self.flush_line_suffixes(queue, stack, Some(element)); } else { - // Only print a newline if the current line isn't already empty - if self.state.line_width > 0 { + if line_mode.is_literal() { self.print_str("\n"); + self.state.pending_indent = Indention::default(); + } else { + // Only print a newline if the current line isn't already empty + if self.state.line_width > 0 { + self.print_str("\n"); + } + + // Print a second line break if this is an empty line + if line_mode == &LineMode::Empty && !self.state.has_empty_line { + self.print_str("\n"); + self.state.has_empty_line = true; + } + + self.state.pending_indent = args.indention(); } - - // Print a second line break if this is an empty line - if line_mode == &LineMode::Empty && !self.state.has_empty_line { - self.print_str("\n"); - self.state.has_empty_line = true; - } - self.state.pending_space = false; - self.state.pending_indent = args.indention(); - // Fit's only tests if groups up to the first line break fit. - // The next group must re-measure if it still fits. - self.state.measured_group_fits = false; + if args.mode().is_flat() { + // Fit's only tests if groups up to the first line break fit. + // The next group must re-measure if it still fits. + self.state.measured_group_fits = false; + } } } FormatElement::ExpandParent => { - // No-op, only has an effect on `fits` - debug_assert!( - !args.mode().is_flat(), - "Fits should always return false for `ExpandParent`" - ); + // Handled in `Document::propagate_expands() } FormatElement::LineSuffixBoundary => { @@ -203,38 +206,40 @@ impl<'a> Printer<'a> { queue.extend_back(content); } - FormatElement::Tag(StartGroup(id)) => { - let group_mode = 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 - stack.push(TagKind::Group, args); - 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 - _ => { - // 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 = fits_on_line(AllPredicate, queue, stack, self)?; - stack.pop(TagKind::Group)?; - - let mode = if fits { - self.state.measured_group_fits = true; + FormatElement::Tag(StartGroup(group)) => { + let group_mode = if !group.mode().is_flat() { + PrintMode::Expanded + } else { + 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 - } else { - PrintMode::Expanded - }; - - stack.push(TagKind::Group, args.with_print_mode(mode)); + } + // 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; - mode + // 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 = fits_on_line(AllPredicate, queue, stack, self)?; + stack.pop(TagKind::Group)?; + + if fits { + PrintMode::Flat + } else { + PrintMode::Expanded + } + } } }; - if let Some(id) = id { - self.state.group_modes.insert_print_mode(*id, group_mode); + stack.push(TagKind::Group, args.with_print_mode(group_mode)); + + if let Some(id) = group.id() { + self.state.group_modes.insert_print_mode(id, group_mode); } } @@ -375,33 +380,32 @@ impl<'a> Printer<'a> { ) -> PrintResult<()> { let args = stack.top(); - if args.mode().is_flat() { + if args.mode().is_flat() && self.state.measured_group_fits { queue.extend_back(best_fitting.most_flat()); self.print_entry(queue, stack, args) } else { + self.state.measured_group_fits = true; + let normal_variants = &best_fitting.variants()[..best_fitting.variants().len() - 1]; - for (index, variant) in normal_variants.iter().enumerate() { + for variant in normal_variants.iter() { // Test if this variant fits and if so, use it. Otherwise try the next // variant. // Try to fit only the first variant on a single line - let mode = if index == 0 { - PrintMode::Flat - } else { - PrintMode::Expanded - }; if !matches!(variant.first(), Some(&FormatElement::Tag(Tag::StartEntry))) { return invalid_start_tag(TagKind::Entry, variant.first()); } + let entry_args = args.with_print_mode(PrintMode::Flat); + // 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. let content = &variant[1..]; queue.extend_back(content); - stack.push(TagKind::Entry, args.with_print_mode(mode)); + stack.push(TagKind::Entry, entry_args); let variant_fits = fits_on_line(AllPredicate, queue, stack, self)?; stack.pop(TagKind::Entry)?; @@ -410,10 +414,8 @@ impl<'a> Printer<'a> { debug_assert_eq!(popped_slice, Some(content)); if variant_fits { - self.state.measured_group_fits = true; - queue.extend_back(variant); - return self.print_entry(queue, stack, args.with_print_mode(mode)); + return self.print_entry(queue, stack, entry_args); } } @@ -444,12 +446,8 @@ impl<'a> Printer<'a> { } // Print the first item - let mut current_fits = self.fits_fill_entry( - SingleEntryPredicate::default(), - queue, - stack, - PrintMode::Flat, - )?; + let mut current_fits = + self.fits_fill_entry(SingleEntryPredicate::default(), queue, stack)?; self.print_entry( queue, @@ -466,12 +464,7 @@ impl<'a> Printer<'a> { // A line break in expanded mode is always necessary if the current item didn't fit. // otherwise see if both contents fit on the line. let all_fits = if current_fits { - self.fits_fill_entry( - SeparatorItemPairPredicate::default(), - queue, - stack, - PrintMode::Flat, - )? + self.fits_fill_entry(SeparatorItemPairPredicate::default(), queue, stack)? } else { false }; @@ -495,12 +488,8 @@ impl<'a> Printer<'a> { self.print_entry(queue, stack, args.with_print_mode(PrintMode::Flat))?; } else { // Test if item fits now - let next_fits = self.fits_fill_entry( - SingleEntryPredicate::default(), - queue, - stack, - PrintMode::Flat, - )?; + let next_fits = + self.fits_fill_entry(SingleEntryPredicate::default(), queue, stack)?; self.print_entry( queue, @@ -528,7 +517,6 @@ impl<'a> Printer<'a> { predicate: P, queue: &mut PrintQueue<'a>, stack: &mut PrintCallStack, - mode: PrintMode, ) -> PrintResult where P: FitsPredicate, @@ -539,10 +527,9 @@ impl<'a> Printer<'a> { return invalid_start_tag(TagKind::Entry, start_entry); } - // Create a virtual group around each fill entry - stack.push(TagKind::Group, stack.top().with_print_mode(mode)); + stack.push(TagKind::Fill, stack.top().with_print_mode(PrintMode::Flat)); let fits = fits_on_line(predicate, queue, stack, self)?; - stack.pop(TagKind::Group)?; + stack.pop(TagKind::Fill)?; Ok(fits) } @@ -796,6 +783,7 @@ where let mut fits_state = FitsState { pending_indent: printer.state.pending_indent, pending_space: printer.state.pending_space, + must_be_flat: matches!(fits_stack.top_kind(), Some(TagKind::Fill)), line_width: printer.state.line_width, has_line_suffix: printer.state.line_suffixes.has_pending(), group_modes: &mut printer.state.group_modes, @@ -880,8 +868,12 @@ fn fits_element_on_line<'a, 'rest>( state.pending_space = true; } LineMode::Soft => {} - LineMode::Hard | LineMode::Empty => { - return Ok(Fits::No); + LineMode::Hard | LineMode::Empty | LineMode::Literal => { + return Ok(if state.must_be_flat { + Fits::No + } else { + Fits::Yes + }); } } } else { @@ -906,10 +898,8 @@ fn fits_element_on_line<'a, 'rest>( let char_width = match c { '\t' => options.tab_width, '\n' => { - return Ok(match args.mode() { - PrintMode::Flat => Fits::No, - PrintMode::Expanded => Fits::Yes, - }) + state.line_width = 0; + 0 } _ => 1, }; @@ -930,17 +920,24 @@ fn fits_element_on_line<'a, 'rest>( } FormatElement::ExpandParent => { - if args.mode().is_flat() { + if state.must_be_flat { return Ok(Fits::No); } } - FormatElement::BestFitting(best_fitting) => match args.mode() { - PrintMode::Flat => { - queue.extend_back(best_fitting.most_flat()); + FormatElement::BestFitting(best_fitting) => { + let slice = match args.mode() { + PrintMode::Flat => best_fitting.most_flat(), + PrintMode::Expanded => best_fitting.most_expanded(), + }; + + if !matches!(slice.first(), Some(FormatElement::Tag(Tag::StartEntry))) { + return invalid_start_tag(TagKind::Entry, slice.first()); } - PrintMode::Expanded => queue.extend_back(best_fitting.most_expanded()), - }, + + stack.push(TagKind::Entry, args.with_print_mode(args.mode())); + queue.extend_back(&slice[1..]); + } FormatElement::Interned(content) => queue.extend_back(content), @@ -963,11 +960,21 @@ fn fits_element_on_line<'a, 'rest>( stack.push(TagKind::Align, args.set_indent_align(align.count())); } - FormatElement::Tag(StartGroup(id)) => { - stack.push(TagKind::Group, args); + FormatElement::Tag(StartGroup(group)) => { + if state.must_be_flat && !group.mode().is_flat() { + return Ok(Fits::No); + } - if let Some(id) = id { - state.group_modes.insert_print_mode(*id, args.mode()); + let group_mode = if !group.mode().is_flat() { + PrintMode::Expanded + } else { + args.mode() + }; + + stack.push(TagKind::Group, args.with_print_mode(group_mode)); + + if let Some(id) = group.id() { + state.group_modes.insert_print_mode(id, group_mode); } } @@ -1097,6 +1104,7 @@ struct FitsState<'group> { pending_indent: Indention, pending_space: bool, has_line_suffix: bool, + must_be_flat: bool, line_width: usize, group_modes: &'group mut GroupModes, } @@ -1107,7 +1115,7 @@ mod tests { use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions}; use crate::{format_args, write, Document, FormatState, IndentStyle, Printed, VecBuffer}; - fn format(root: &dyn Format<()>) -> Printed { + fn format(root: &dyn Format) -> Printed { format_with_options( root, PrinterOptions { @@ -1117,14 +1125,14 @@ mod tests { ) } - fn format_with_options(root: &dyn Format<()>, options: PrinterOptions) -> Printed { - let mut state = FormatState::new(()); - let mut buffer = VecBuffer::new(&mut state); - - write!(&mut buffer, [root]).unwrap(); + fn format_with_options( + root: &dyn Format, + options: PrinterOptions, + ) -> Printed { + let formatted = crate::format!(SimpleFormatContext::default(), [root]).unwrap(); Printer::new(options) - .print(&Document::from(buffer.into_vec())) + .print(formatted.document()) .expect("Document to be valid") } @@ -1195,10 +1203,14 @@ a"#, } #[test] - fn it_breaks_a_group_if_a_string_contains_a_newline() { + fn it_breaks_a_group_if_it_contains_a_literal_line() { let result = format(&FormatArrayElements { items: vec![ - &text("`This is a string spanning\ntwo lines`"), + &format_args![ + text("`This is a string spanning"), + &literal_line(), + &text("two lines`") + ], &text("\"b\""), ], }); @@ -1470,11 +1482,11 @@ Group 1 breaks"# } struct FormatArrayElements<'a> { - items: Vec<&'a dyn Format<()>>, + items: Vec<&'a dyn Format>, } - impl Format<()> for FormatArrayElements<'_> { - fn fmt(&self, f: &mut Formatter<()>) -> FormatResult<()> { + impl Format for FormatArrayElements<'_> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { write!( f, [group(&format_args!( diff --git a/crates/rome_formatter/src/verbatim.rs b/crates/rome_formatter/src/verbatim.rs index 60da1b60c43..c3fd064636e 100644 --- a/crates/rome_formatter/src/verbatim.rs +++ b/crates/rome_formatter/src/verbatim.rs @@ -1,8 +1,12 @@ use crate::format_element::tag::VerbatimKind; use crate::prelude::*; use crate::trivia::{FormatLeadingComments, FormatTrailingComments}; -use crate::{write, CstFormatContext}; -use rome_rowan::{Direction, Language, SyntaxElement, SyntaxNode, TextRange}; +use crate::{write, CstFormatContext, TextLen, TextSize}; +use rome_rowan::{ + Direction, Language, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTokenText, TextRange, +}; +use std::iter::FusedIterator; +use std::str::CharIndices; /// "Formats" a node according to its original formatting in the source text. Being able to format /// a node "as is" is useful if a node contains syntax errors. Formatting a node with syntax errors @@ -108,8 +112,9 @@ where }, ); - dynamic_text( - &normalize_newlines(&original_source, LINE_TERMINATORS), + normalize_newlines( + &original_source, + LINE_TERMINATORS, self.node.text_trimmed_range().start(), ) .fmt(f)?; @@ -167,3 +172,209 @@ pub fn format_suppressed_node(node: &SyntaxNode) -> FormatVerbat format_comments: true, } } + +/// Replace the line terminators matching the provided list with "literalline". +pub const fn normalize_newlines( + text: &str, + terminators: [char; N], + source_position: TextSize, +) -> NormalizeNewLines { + NormalizeNewLines { + text, + terminators, + source_position, + } +} + +const LINE_SEPARATOR: char = '\u{2028}'; +const PARAGRAPH_SEPARATOR: char = '\u{2029}'; +pub const LINE_TERMINATORS: [char; 4] = ['\n', '\r', LINE_SEPARATOR, PARAGRAPH_SEPARATOR]; + +pub struct NormalizeNewLines<'a, const N: usize> { + text: &'a str, + source_position: TextSize, + terminators: [char; N], +} + +impl Format for NormalizeNewLines<'_, N> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + for part in NewlineParts::new(self.text, self.terminators) { + match part { + NewlineOrText::Newline => { + write!(f, [literal_line()])?; + } + NewlineOrText::Text((start, text)) => { + write!(f, [dynamic_text(text, self.source_position + start)])?; + } + } + } + + Ok(()) + } +} + +/// Replace the line terminators matching the provided list with "literalline". +pub fn normalize_token_text_new_lines( + token: &SyntaxToken, + range: TextRange, + terminators: [char; N], +) -> NormalizeTokenTextNewLines { + let relative_range = range - token.text_range().start(); + let text = token.token_text().slice(relative_range); + + NormalizeTokenTextNewLines { + text, + terminators, + source_position: range.start(), + } +} + +pub struct NormalizeTokenTextNewLines { + pub text: SyntaxTokenText, + pub terminators: [char; N], + pub source_position: TextSize, +} + +impl Format for NormalizeTokenTextNewLines { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + for part in NewlineParts::new(&self.text, self.terminators) { + match part { + NewlineOrText::Newline => { + write!(f, [literal_line()])?; + } + NewlineOrText::Text((start, text)) => { + f.write_element(FormatElement::Text(Text::SyntaxTokenTextSlice { + source_position: self.source_position + start, + slice: self.text.clone().slice(TextRange::at( + start + self.text.range().start(), + text.text_len(), + )), + }))?; + } + } + } + + Ok(()) + } +} + +struct NewlineParts<'a, const N: usize> { + text: &'a str, + chars: std::iter::Peekable>, + terminators: [char; N], +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum NewlineOrText<'a> { + Newline, + Text((TextSize, &'a str)), +} + +impl<'a, const N: usize> NewlineParts<'a, N> { + fn new(text: &'a str, terminators: [char; N]) -> Self { + Self { + chars: text.char_indices().peekable(), + text, + terminators, + } + } +} + +impl<'a, const N: usize> Iterator for NewlineParts<'a, N> { + type Item = NewlineOrText<'a>; + + fn next(&mut self) -> Option { + let (start, char) = self.chars.next()?; + + if self.terminators.contains(&char) { + // If the current character is \r and the + // next is \n, skip over the entire sequence + if char == '\r' && matches!(self.chars.peek(), Some((_, '\n'))) { + self.chars.next(); + } + + Some(NewlineOrText::Newline) + } else { + let start_position = TextSize::from(start as u32); + loop { + match self.chars.peek() { + Some((index, next)) => { + if self.terminators.contains(next) { + break Some(NewlineOrText::Text(( + start_position, + &self.text[start..*index], + ))); + } + + self.chars.next(); + } + None => break Some(NewlineOrText::Text((start_position, &self.text[start..]))), + } + } + } + } +} + +impl FusedIterator for NewlineParts<'_, N> {} + +#[cfg(test)] +mod tests { + + use super::{NewlineOrText, NewlineParts, LINE_TERMINATORS}; + use crate::TextSize; + + #[test] + fn test_normalize_newlines() { + assert_eq!( + NewlineParts::new("a\nb", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(2), "b")) + ] + ); + assert_eq!( + NewlineParts::new("a\n\n\nb", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Newline, + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(4), "b")) + ] + ); + + assert_eq!( + NewlineParts::new("a\rb", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(2), "b")) + ] + ); + assert_eq!( + NewlineParts::new("a\r\nb", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(3), "b")) + ] + ); + assert_eq!( + NewlineParts::new("a\u{2028}b", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(4), "b")) + ] + ); + assert_eq!( + NewlineParts::new("a\u{2029}b", LINE_TERMINATORS).collect::>(), + vec![ + NewlineOrText::Text((TextSize::from(0), "a")), + NewlineOrText::Newline, + NewlineOrText::Text((TextSize::from(4), "b")) + ] + ); + } +} diff --git a/crates/rome_js_formatter/src/js/expressions/array_expression.rs b/crates/rome_js_formatter/src/js/expressions/array_expression.rs index 901284c6ee8..c541897087b 100644 --- a/crates/rome_js_formatter/src/js/expressions/array_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/array_expression.rs @@ -30,25 +30,19 @@ impl FormatNodeRule for FormatJsArrayExpression { r_brack_token.format(), ] ) - } else if should_break(&elements)? { - write!( - f, - [ - l_brack_token.format(), - block_indent(&elements.format()), - r_brack_token.format() - ] - ) } else { let group_id = f.group_id("array"); + let should_expand = should_break(&elements)?; let elements = elements.format().with_options(Some(group_id)); write!( f, [ l_brack_token.format(), - group(&soft_block_indent(&elements)).with_group_id(Some(group_id)), + group(&soft_block_indent(&elements)) + .with_group_id(Some(group_id)) + .should_expand(should_expand), r_brack_token.format() ] ) diff --git a/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs b/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs index 28ee4419386..d27ce7ae106 100644 --- a/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs +++ b/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs @@ -38,13 +38,9 @@ impl Format for AnyTemplateChunkElement { f, [format_replaced( &chunk, - &syntax_token_cow_slice( - // Per https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-static-semantics-trv: - // In template literals, the '\r' and '\r\n' line terminators are normalized to '\n' - normalize_newlines(chunk.text_trimmed(), ['\r']), - &chunk, - chunk.text_trimmed_range().start(), - ) + // Per https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-static-semantics-trv: + // In template literals, the '\r' and '\r\n' line terminators are normalized to '\n' + &normalize_token_text_new_lines(&chunk, chunk.text_trimmed_range(), ['\n', '\r']) )] ) } diff --git a/crates/rome_js_formatter/src/js/lists/directive_list.rs b/crates/rome_js_formatter/src/js/lists/directive_list.rs index 01cbddd7833..60c4306145a 100644 --- a/crates/rome_js_formatter/src/js/lists/directive_list.rs +++ b/crates/rome_js_formatter/src/js/lists/directive_list.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use rome_formatter::{get_lines_before, write}; +use rome_formatter::write; use rome_js_syntax::JsDirectiveList; use rome_rowan::{AstNode, AstNodeList}; diff --git a/crates/rome_js_formatter/src/jsx/auxiliary/expression_child.rs b/crates/rome_js_formatter/src/jsx/auxiliary/expression_child.rs index 9d921cebec7..ef7594a4aa9 100644 --- a/crates/rome_js_formatter/src/jsx/auxiliary/expression_child.rs +++ b/crates/rome_js_formatter/src/jsx/auxiliary/expression_child.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use crate::prelude::{format_args, write}; use crate::utils::JsAnyBinaryLikeExpression; -use rome_formatter::{group, CstFormatContext, FormatResult}; +use rome_formatter::{CstFormatContext, FormatResult}; use rome_js_syntax::{JsAnyExpression, JsxExpressionChild, JsxExpressionChildFields}; #[derive(Debug, Clone, Default)] diff --git a/crates/rome_js_formatter/src/jsx/lists/child_list.rs b/crates/rome_js_formatter/src/jsx/lists/child_list.rs index ee9b9027b10..d36811fbdd2 100644 --- a/crates/rome_js_formatter/src/jsx/lists/child_list.rs +++ b/crates/rome_js_formatter/src/jsx/lists/child_list.rs @@ -4,7 +4,7 @@ use crate::utils::jsx::{ JsxRawSpace, JsxSpace, }; use crate::JsFormatter; -use rome_formatter::format_element::tag::Tag; +use rome_formatter::format_element::tag::{GroupMode, Tag}; use rome_formatter::{format_args, write, CstFormatContext, FormatRuleWithOptions, VecBuffer}; use rome_js_syntax::{JsxAnyChild, JsxChildList}; use std::cell::RefCell; @@ -539,7 +539,13 @@ impl Format for FormatMultilineChildren { elements, FormatElement::Tag(Tag::EndFill), ])?, - MultilineLayout::NoFill => f.write_element(elements)?, + MultilineLayout::NoFill => f.write_elements([ + FormatElement::Tag(Tag::StartGroup( + tag::Group::new().with_mode(GroupMode::Expand), + )), + elements, + FormatElement::Tag(Tag::EndGroup), + ])?, }; } diff --git a/crates/rome_js_formatter/src/utils/binary_like_expression.rs b/crates/rome_js_formatter/src/utils/binary_like_expression.rs index e22948e207a..db04a762fb0 100644 --- a/crates/rome_js_formatter/src/utils/binary_like_expression.rs +++ b/crates/rome_js_formatter/src/utils/binary_like_expression.rs @@ -367,8 +367,11 @@ impl Format for BinaryLeftOrRightSide { )?; } - if !should_break && should_group { - write!(f, [group(&operator_and_right_expression)])?; + if should_group { + write!( + f, + [group(&operator_and_right_expression).should_expand(should_break)] + )?; } else { write!(f, [operator_and_right_expression])?; } diff --git a/crates/rome_js_formatter/src/utils/jsx.rs b/crates/rome_js_formatter/src/utils/jsx.rs index bb91647c55a..5e37f437f4e 100644 --- a/crates/rome_js_formatter/src/utils/jsx.rs +++ b/crates/rome_js_formatter/src/utils/jsx.rs @@ -168,7 +168,7 @@ impl Format for JsxSpace { write![ formatter, [ - if_group_breaks(&format_args![JsxRawSpace, hard_line_break()]), + if_group_breaks(&format_args![JsxRawSpace, soft_line_break()]), if_group_fits_on_line(&space()) ] ] diff --git a/crates/rome_js_formatter/src/utils/object_like.rs b/crates/rome_js_formatter/src/utils/object_like.rs index 44f2964c664..c1bc70da9a6 100644 --- a/crates/rome_js_formatter/src/utils/object_like.rs +++ b/crates/rome_js_formatter/src/utils/object_like.rs @@ -60,10 +60,12 @@ impl Format for JsObjectLike { f, [format_dangling_comments(self.syntax()).with_block_indent(),] )?; - } else if self.members_have_leading_newline() { - write!(f, [block_indent(&members)])?; } else { - write!(f, [group(&soft_space_or_block_indent(&members))])?; + let should_expand = self.members_have_leading_newline(); + write!( + f, + [group(&soft_space_or_block_indent(&members)).should_expand(should_expand)] + )?; } write!(f, [self.r_curly_token().format()]) diff --git a/crates/rome_js_formatter/src/utils/string_utils.rs b/crates/rome_js_formatter/src/utils/string_utils.rs index 1907a456d4e..0c65c1be144 100644 --- a/crates/rome_js_formatter/src/utils/string_utils.rs +++ b/crates/rome_js_formatter/src/utils/string_utils.rs @@ -105,15 +105,20 @@ impl CleanedStringLiteralText<'_> { impl Format for CleanedStringLiteralText<'_> { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - format_replaced( - self.token, - &syntax_token_cow_slice( - self.text.clone(), + let format_text = format_with(|f| match &self.text { + Cow::Borrowed(_) => normalize_token_text_new_lines( self.token, - self.token.text_trimmed_range().start(), - ), - ) - .fmt(f) + self.token.text_trimmed_range(), + ['\n', '\r'], + ) + .fmt(f), + Cow::Owned(text) => { + normalize_newlines(text, ['\n', '\r'], self.token.text_trimmed_range().start()) + .fmt(f) + } + }); + + format_replaced(self.token, &format_text).fmt(f) } } @@ -399,11 +404,6 @@ impl<'token> LiteralStringNormaliser<'token> { /// as opposed to the previous example, we have a signal that says that we should keep the current /// character. Then we do so. The third iteration comes along and we find `'`. We still have the /// [CharSignal::Keep]. We do so and then we set the signal to [CharSignal::Idle] - /// - /// # Newlines - /// - /// By default the formatter uses `\n` as a newline. The function replaces - /// `\r\n` with `\n`, fn normalize_string(&self, string_information: &StringInformation) -> Cow<'token, str> { let raw_content = self.raw_content(); @@ -483,21 +483,15 @@ impl<'token> LiteralStringNormaliser<'token> { reduced_string.push(current_char); } } - '\n' | '\t' => { + '\t' => { if let AlreadyPrinted(the_char) = signal { - if matches!(the_char, '\n' | '\t') { + if matches!(the_char, '\t') { signal = CharSignal::None } } else { reduced_string.push(current_char); } } - // If the current character is \r and the - // next is \n, skip over the entire sequence - '\r' if next_character.map_or(false, |(_, c)| *c == '\n') => { - reduced_string.push('\n'); - signal = AlreadyPrinted('\n'); - } _ => { // If we encounter a preferred quote and it's not escaped, we have to replace it with // an escaped version. From 9bc4462283eaa9ab3fae1d14458398f9b7517c3a Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 27 Sep 2022 14:32:51 +0200 Subject: [PATCH 2/5] Undo literalline --- crates/rome_formatter/src/builders.rs | 32 --- crates/rome_formatter/src/format_element.rs | 52 +++-- .../src/format_element/document.rs | 8 +- crates/rome_formatter/src/lib.rs | 10 +- crates/rome_formatter/src/prelude.rs | 5 +- crates/rome_formatter/src/printer/mod.rs | 51 ++-- crates/rome_formatter/src/verbatim.rs | 219 +----------------- .../js/expressions/template_chunk_element.rs | 10 +- .../src/utils/string_utils.rs | 36 +-- 9 files changed, 100 insertions(+), 323 deletions(-) diff --git a/crates/rome_formatter/src/builders.rs b/crates/rome_formatter/src/builders.rs index 2331318b18f..c66b0378303 100644 --- a/crates/rome_formatter/src/builders.rs +++ b/crates/rome_formatter/src/builders.rs @@ -100,38 +100,6 @@ pub const fn hard_line_break() -> Line { Line::new(LineMode::Hard) } -/// A plain line break that does not add any indent. -/// Useful for line breaks in a spacing sensitive context. -/// -/// # Examples -/// -/// ``` -/// use rome_formatter::{format, format_args}; -/// use rome_formatter::prelude::*; -/// -/// # fn main() -> FormatResult<()> { -/// let block = format!(SimpleFormatContext::default(), [ -/// text("a ="), -/// block_indent(&format_args![ -/// text("`abcd"), -/// literal_line(), -/// text("...no indent at the beginning`") -/// ]), -/// text(";"), -/// ])?; -/// -/// assert_eq!( -/// "a =\n\t`abcd\n...no indent at the beginning`\n;", -/// block.print()?.as_code() -/// ); -/// # Ok(()) -/// # } -/// ``` -#[inline] -pub const fn literal_line() -> Line { - Line::new(LineMode::Literal) -} - /// A forced empty line. An empty line inserts enough line breaks in the output for /// the previous and next element to be separated by an empty line. /// diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs index e12e4db0b28..7b090762f01 100644 --- a/crates/rome_formatter/src/format_element.rs +++ b/crates/rome_formatter/src/format_element.rs @@ -2,6 +2,7 @@ pub mod document; pub mod tag; use crate::format_element::tag::{LabelId, Tag}; +use std::borrow::Cow; use crate::{TagKind, TextSize}; #[cfg(target_pointer_width = "64")] @@ -71,8 +72,6 @@ pub enum LineMode { Soft, /// See [crate::builders::hard_line_break] for documentation. Hard, - /// See [crate::builders::literal_line] for documentation. - Literal, /// See [crate::builders::empty_line] for documentation. Empty, } @@ -81,10 +80,6 @@ impl LineMode { pub const fn is_hard(&self) -> bool { matches!(self, LineMode::Hard) } - - pub const fn is_literal(&self) -> bool { - matches!(self, LineMode::Literal) - } } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -207,6 +202,38 @@ impl PartialEq for Text { } } +const LINE_SEPARATOR: char = '\u{2028}'; +const PARAGRAPH_SEPARATOR: char = '\u{2029}'; +pub const LINE_TERMINATORS: [char; 3] = ['\r', LINE_SEPARATOR, PARAGRAPH_SEPARATOR]; + +/// Replace the line terminators matching the provided list with "\n" +/// since its the only line break type supported by the printer +pub fn normalize_newlines(text: &str, terminators: [char; N]) -> Cow { + let mut result = String::new(); + let mut last_end = 0; + + for (start, part) in text.match_indices(terminators) { + result.push_str(&text[last_end..start]); + result.push('\n'); + + last_end = start + part.len(); + // If the current character is \r and the + // next is \n, skip over the entire sequence + if part == "\r" && text[last_end..].starts_with('\n') { + last_end += 1; + } + } + + // If the result is empty no line terminators were matched, + // return the entire input text without allocating a new String + if result.is_empty() { + Cow::Borrowed(text) + } else { + result.push_str(&text[last_end..text.len()]); + Cow::Owned(result) + } +} + impl Deref for Text { type Target = str; fn deref(&self) -> &Self::Target { @@ -248,18 +275,15 @@ impl FormatElements for FormatElement { match self { FormatElement::ExpandParent => true, FormatElement::Tag(Tag::StartGroup(group)) => !group.mode().is_flat(), - FormatElement::Line(line_mode) => matches!( - line_mode, - LineMode::Hard | LineMode::Empty | LineMode::Literal - ), + FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty), + FormatElement::Text(text) => text.contains('\n'), FormatElement::Interned(interned) => interned.will_break(), // Traverse into the most flat version because the content is guaranteed to expand when even // the most flat version contains some content that forces a break. FormatElement::BestFitting(best_fitting) => best_fitting.most_flat().will_break(), - FormatElement::LineSuffixBoundary - | FormatElement::Space - | FormatElement::Tag(_) - | FormatElement::Text(_) => false, + FormatElement::LineSuffixBoundary | FormatElement::Space | FormatElement::Tag(_) => { + false + } } } diff --git a/crates/rome_formatter/src/format_element/document.rs b/crates/rome_formatter/src/format_element/document.rs index 771bb9264b5..15e93e4a65f 100644 --- a/crates/rome_formatter/src/format_element/document.rs +++ b/crates/rome_formatter/src/format_element/document.rs @@ -79,10 +79,9 @@ impl Document { enclosing.pop(); continue; } + FormatElement::Text(text) => text.contains('\n'), FormatElement::ExpandParent - | FormatElement::Line(LineMode::Hard | LineMode::Empty | LineMode::Literal) => { - true - } + | FormatElement::Line(LineMode::Hard | LineMode::Empty) => true, _ => false, }; @@ -229,9 +228,6 @@ impl Format for &[FormatElement] { LineMode::Empty => { write!(f, [text("empty_line")])?; } - LineMode::Literal => { - write!(f, [text("literal_line")])?; - } }, FormatElement::ExpandParent => { write!(f, [text("expand_parent")])?; diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs index e4cec89773d..68e86f5dbab 100644 --- a/crates/rome_formatter/src/lib.rs +++ b/crates/rome_formatter/src/lib.rs @@ -50,9 +50,9 @@ pub use arguments::{Argument, Arguments}; pub use buffer::{Buffer, BufferExtensions, BufferSnapshot, Inspect, PreambleBuffer, VecBuffer}; pub use builders::BestFitting; +use crate::builders::syntax_token_cow_slice; use crate::comments::{CommentStyle, Comments, SourceComment}; -use crate::verbatim::{normalize_token_text_new_lines, LINE_TERMINATORS}; -pub use format_element::{FormatElement, Text}; +pub use format_element::{normalize_newlines, FormatElement, Text, LINE_TERMINATORS}; pub use group_id::GroupId; use rome_rowan::{ Language, SyntaxElement, SyntaxError, SyntaxNode, SyntaxResult, SyntaxToken, SyntaxTriviaPiece, @@ -1400,10 +1400,10 @@ impl Format for SyntaxTriviaPiece { write!( f, - [normalize_token_text_new_lines( + [syntax_token_cow_slice( + normalize_newlines(trimmed, LINE_TERMINATORS), &self.token(), - TextRange::at(trimmed_start, trimmed.text_len()), - LINE_TERMINATORS + trimmed_start )] ) } diff --git a/crates/rome_formatter/src/prelude.rs b/crates/rome_formatter/src/prelude.rs index 5870b9c0f51..1a3a276a991 100644 --- a/crates/rome_formatter/src/prelude.rs +++ b/crates/rome_formatter/src/prelude.rs @@ -8,10 +8,7 @@ pub use crate::trivia::{ format_replaced, format_trailing_comments, format_trimmed_token, }; -pub use crate::verbatim::{ - format_suppressed_node, format_unknown_node, format_verbatim_node, normalize_newlines, - normalize_token_text_new_lines, LINE_TERMINATORS, -}; +pub use crate::verbatim::{format_suppressed_node, format_unknown_node, format_verbatim_node}; pub use crate::format_element::document::Document; pub use crate::format_element::tag::{LabelId, Tag, TagKind}; diff --git a/crates/rome_formatter/src/printer/mod.rs b/crates/rome_formatter/src/printer/mod.rs index 177b6772719..d05aae65442 100644 --- a/crates/rome_formatter/src/printer/mod.rs +++ b/crates/rome_formatter/src/printer/mod.rs @@ -162,30 +162,19 @@ impl<'a> Printer<'a> { } else if self.state.line_suffixes.has_pending() { self.flush_line_suffixes(queue, stack, Some(element)); } else { - if line_mode.is_literal() { + // Only print a newline if the current line isn't already empty + if self.state.line_width > 0 { self.print_str("\n"); - self.state.pending_indent = Indention::default(); - } else { - // Only print a newline if the current line isn't already empty - if self.state.line_width > 0 { - self.print_str("\n"); - } - - // Print a second line break if this is an empty line - if line_mode == &LineMode::Empty && !self.state.has_empty_line { - self.print_str("\n"); - self.state.has_empty_line = true; - } - - self.state.pending_indent = args.indention(); } - self.state.pending_space = false; - if args.mode().is_flat() { - // Fit's only tests if groups up to the first line break fit. - // The next group must re-measure if it still fits. - self.state.measured_group_fits = false; + // Print a second line break if this is an empty line + if line_mode == &LineMode::Empty && !self.state.has_empty_line { + self.print_str("\n"); + self.state.has_empty_line = true; } + + self.state.pending_space = false; + self.state.pending_indent = args.indention(); } } @@ -596,9 +585,14 @@ impl<'a> Printer<'a> { self.state .buffer .push_str(self.options.line_ending.as_str()); + self.state.generated_line += 1; self.state.generated_column = 0; self.state.line_width = 0; + + // Fit's only tests if groups up to the first line break fit. + // The next group must re-measure if it still fits. + self.state.measured_group_fits = false; } else { self.state.buffer.push(char); self.state.generated_column += 1; @@ -868,7 +862,7 @@ fn fits_element_on_line<'a, 'rest>( state.pending_space = true; } LineMode::Soft => {} - LineMode::Hard | LineMode::Empty | LineMode::Literal => { + LineMode::Hard | LineMode::Empty => { return Ok(if state.must_be_flat { Fits::No } else { @@ -898,8 +892,11 @@ fn fits_element_on_line<'a, 'rest>( let char_width = match c { '\t' => options.tab_width, '\n' => { - state.line_width = 0; - 0 + return Ok(if state.must_be_flat { + Fits::No + } else { + Fits::Yes + }); } _ => 1, }; @@ -1203,14 +1200,10 @@ a"#, } #[test] - fn it_breaks_a_group_if_it_contains_a_literal_line() { + fn it_breaks_a_group_if_a_string_contains_a_newline() { let result = format(&FormatArrayElements { items: vec![ - &format_args![ - text("`This is a string spanning"), - &literal_line(), - &text("two lines`") - ], + &text("`This is a string spanning\ntwo lines`"), &text("\"b\""), ], }); diff --git a/crates/rome_formatter/src/verbatim.rs b/crates/rome_formatter/src/verbatim.rs index c3fd064636e..60da1b60c43 100644 --- a/crates/rome_formatter/src/verbatim.rs +++ b/crates/rome_formatter/src/verbatim.rs @@ -1,12 +1,8 @@ use crate::format_element::tag::VerbatimKind; use crate::prelude::*; use crate::trivia::{FormatLeadingComments, FormatTrailingComments}; -use crate::{write, CstFormatContext, TextLen, TextSize}; -use rome_rowan::{ - Direction, Language, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTokenText, TextRange, -}; -use std::iter::FusedIterator; -use std::str::CharIndices; +use crate::{write, CstFormatContext}; +use rome_rowan::{Direction, Language, SyntaxElement, SyntaxNode, TextRange}; /// "Formats" a node according to its original formatting in the source text. Being able to format /// a node "as is" is useful if a node contains syntax errors. Formatting a node with syntax errors @@ -112,9 +108,8 @@ where }, ); - normalize_newlines( - &original_source, - LINE_TERMINATORS, + dynamic_text( + &normalize_newlines(&original_source, LINE_TERMINATORS), self.node.text_trimmed_range().start(), ) .fmt(f)?; @@ -172,209 +167,3 @@ pub fn format_suppressed_node(node: &SyntaxNode) -> FormatVerbat format_comments: true, } } - -/// Replace the line terminators matching the provided list with "literalline". -pub const fn normalize_newlines( - text: &str, - terminators: [char; N], - source_position: TextSize, -) -> NormalizeNewLines { - NormalizeNewLines { - text, - terminators, - source_position, - } -} - -const LINE_SEPARATOR: char = '\u{2028}'; -const PARAGRAPH_SEPARATOR: char = '\u{2029}'; -pub const LINE_TERMINATORS: [char; 4] = ['\n', '\r', LINE_SEPARATOR, PARAGRAPH_SEPARATOR]; - -pub struct NormalizeNewLines<'a, const N: usize> { - text: &'a str, - source_position: TextSize, - terminators: [char; N], -} - -impl Format for NormalizeNewLines<'_, N> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for part in NewlineParts::new(self.text, self.terminators) { - match part { - NewlineOrText::Newline => { - write!(f, [literal_line()])?; - } - NewlineOrText::Text((start, text)) => { - write!(f, [dynamic_text(text, self.source_position + start)])?; - } - } - } - - Ok(()) - } -} - -/// Replace the line terminators matching the provided list with "literalline". -pub fn normalize_token_text_new_lines( - token: &SyntaxToken, - range: TextRange, - terminators: [char; N], -) -> NormalizeTokenTextNewLines { - let relative_range = range - token.text_range().start(); - let text = token.token_text().slice(relative_range); - - NormalizeTokenTextNewLines { - text, - terminators, - source_position: range.start(), - } -} - -pub struct NormalizeTokenTextNewLines { - pub text: SyntaxTokenText, - pub terminators: [char; N], - pub source_position: TextSize, -} - -impl Format for NormalizeTokenTextNewLines { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for part in NewlineParts::new(&self.text, self.terminators) { - match part { - NewlineOrText::Newline => { - write!(f, [literal_line()])?; - } - NewlineOrText::Text((start, text)) => { - f.write_element(FormatElement::Text(Text::SyntaxTokenTextSlice { - source_position: self.source_position + start, - slice: self.text.clone().slice(TextRange::at( - start + self.text.range().start(), - text.text_len(), - )), - }))?; - } - } - } - - Ok(()) - } -} - -struct NewlineParts<'a, const N: usize> { - text: &'a str, - chars: std::iter::Peekable>, - terminators: [char; N], -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum NewlineOrText<'a> { - Newline, - Text((TextSize, &'a str)), -} - -impl<'a, const N: usize> NewlineParts<'a, N> { - fn new(text: &'a str, terminators: [char; N]) -> Self { - Self { - chars: text.char_indices().peekable(), - text, - terminators, - } - } -} - -impl<'a, const N: usize> Iterator for NewlineParts<'a, N> { - type Item = NewlineOrText<'a>; - - fn next(&mut self) -> Option { - let (start, char) = self.chars.next()?; - - if self.terminators.contains(&char) { - // If the current character is \r and the - // next is \n, skip over the entire sequence - if char == '\r' && matches!(self.chars.peek(), Some((_, '\n'))) { - self.chars.next(); - } - - Some(NewlineOrText::Newline) - } else { - let start_position = TextSize::from(start as u32); - loop { - match self.chars.peek() { - Some((index, next)) => { - if self.terminators.contains(next) { - break Some(NewlineOrText::Text(( - start_position, - &self.text[start..*index], - ))); - } - - self.chars.next(); - } - None => break Some(NewlineOrText::Text((start_position, &self.text[start..]))), - } - } - } - } -} - -impl FusedIterator for NewlineParts<'_, N> {} - -#[cfg(test)] -mod tests { - - use super::{NewlineOrText, NewlineParts, LINE_TERMINATORS}; - use crate::TextSize; - - #[test] - fn test_normalize_newlines() { - assert_eq!( - NewlineParts::new("a\nb", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(2), "b")) - ] - ); - assert_eq!( - NewlineParts::new("a\n\n\nb", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Newline, - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(4), "b")) - ] - ); - - assert_eq!( - NewlineParts::new("a\rb", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(2), "b")) - ] - ); - assert_eq!( - NewlineParts::new("a\r\nb", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(3), "b")) - ] - ); - assert_eq!( - NewlineParts::new("a\u{2028}b", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(4), "b")) - ] - ); - assert_eq!( - NewlineParts::new("a\u{2029}b", LINE_TERMINATORS).collect::>(), - vec![ - NewlineOrText::Text((TextSize::from(0), "a")), - NewlineOrText::Newline, - NewlineOrText::Text((TextSize::from(4), "b")) - ] - ); - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs b/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs index d27ce7ae106..28ee4419386 100644 --- a/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs +++ b/crates/rome_js_formatter/src/js/expressions/template_chunk_element.rs @@ -38,9 +38,13 @@ impl Format for AnyTemplateChunkElement { f, [format_replaced( &chunk, - // Per https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-static-semantics-trv: - // In template literals, the '\r' and '\r\n' line terminators are normalized to '\n' - &normalize_token_text_new_lines(&chunk, chunk.text_trimmed_range(), ['\n', '\r']) + &syntax_token_cow_slice( + // Per https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-static-semantics-trv: + // In template literals, the '\r' and '\r\n' line terminators are normalized to '\n' + normalize_newlines(chunk.text_trimmed(), ['\r']), + &chunk, + chunk.text_trimmed_range().start(), + ) )] ) } diff --git a/crates/rome_js_formatter/src/utils/string_utils.rs b/crates/rome_js_formatter/src/utils/string_utils.rs index 0c65c1be144..1907a456d4e 100644 --- a/crates/rome_js_formatter/src/utils/string_utils.rs +++ b/crates/rome_js_formatter/src/utils/string_utils.rs @@ -105,20 +105,15 @@ impl CleanedStringLiteralText<'_> { impl Format for CleanedStringLiteralText<'_> { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let format_text = format_with(|f| match &self.text { - Cow::Borrowed(_) => normalize_token_text_new_lines( + format_replaced( + self.token, + &syntax_token_cow_slice( + self.text.clone(), self.token, - self.token.text_trimmed_range(), - ['\n', '\r'], - ) - .fmt(f), - Cow::Owned(text) => { - normalize_newlines(text, ['\n', '\r'], self.token.text_trimmed_range().start()) - .fmt(f) - } - }); - - format_replaced(self.token, &format_text).fmt(f) + self.token.text_trimmed_range().start(), + ), + ) + .fmt(f) } } @@ -404,6 +399,11 @@ impl<'token> LiteralStringNormaliser<'token> { /// as opposed to the previous example, we have a signal that says that we should keep the current /// character. Then we do so. The third iteration comes along and we find `'`. We still have the /// [CharSignal::Keep]. We do so and then we set the signal to [CharSignal::Idle] + /// + /// # Newlines + /// + /// By default the formatter uses `\n` as a newline. The function replaces + /// `\r\n` with `\n`, fn normalize_string(&self, string_information: &StringInformation) -> Cow<'token, str> { let raw_content = self.raw_content(); @@ -483,15 +483,21 @@ impl<'token> LiteralStringNormaliser<'token> { reduced_string.push(current_char); } } - '\t' => { + '\n' | '\t' => { if let AlreadyPrinted(the_char) = signal { - if matches!(the_char, '\t') { + if matches!(the_char, '\n' | '\t') { signal = CharSignal::None } } else { reduced_string.push(current_char); } } + // If the current character is \r and the + // next is \n, skip over the entire sequence + '\r' if next_character.map_or(false, |(_, c)| *c == '\n') => { + reduced_string.push('\n'); + signal = AlreadyPrinted('\n'); + } _ => { // If we encounter a preferred quote and it's not escaped, we have to replace it with // an escaped version. From 07b0571efa3cee240b544804952b3de593a55a72 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 27 Sep 2022 14:52:01 +0200 Subject: [PATCH 3/5] Restore tests --- crates/rome_formatter/src/format_element.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs index 7b090762f01..e289a1c1634 100644 --- a/crates/rome_formatter/src/format_element.rs +++ b/crates/rome_formatter/src/format_element.rs @@ -394,6 +394,26 @@ pub trait FormatElements { fn end_tag(&self, kind: TagKind) -> Option<&Tag>; } +#[cfg(test)] +mod tests { + + use crate::format_element::{normalize_newlines, LINE_TERMINATORS}; + + #[test] + fn test_normalize_newlines() { + assert_eq!(normalize_newlines("a\nb", LINE_TERMINATORS), "a\nb"); + assert_eq!(normalize_newlines("a\n\n\nb", LINE_TERMINATORS), "a\n\n\nb"); + assert_eq!(normalize_newlines("a\rb", LINE_TERMINATORS), "a\nb"); + assert_eq!(normalize_newlines("a\r\nb", LINE_TERMINATORS), "a\nb"); + assert_eq!( + normalize_newlines("a\r\n\r\n\r\nb", LINE_TERMINATORS), + "a\n\n\nb" + ); + assert_eq!(normalize_newlines("a\u{2028}b", LINE_TERMINATORS), "a\nb"); + assert_eq!(normalize_newlines("a\u{2029}b", LINE_TERMINATORS), "a\nb"); + } +} + #[cfg(target_pointer_width = "64")] static_assert!(std::mem::size_of::() == 8usize); From 187e9a442e642d4de68ae3a84f3b373ea6784151 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 28 Sep 2022 16:12:22 +0100 Subject: [PATCH 4/5] Update crates/rome_formatter/src/format_element/document.rs Co-authored-by: Emanuele Stoppa --- crates/rome_formatter/src/format_element/document.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rome_formatter/src/format_element/document.rs b/crates/rome_formatter/src/format_element/document.rs index 15e93e4a65f..8da0d887a8f 100644 --- a/crates/rome_formatter/src/format_element/document.rs +++ b/crates/rome_formatter/src/format_element/document.rs @@ -28,7 +28,7 @@ impl Document { /// [`BestFitting`] elements act as expand boundaries, meaning that the fact that a /// [`BestFitting`]'s content expands is not propagated past the [`BestFitting`] element. /// - /// [`BestFitting`](FormatElement::BestFitting) + /// [`BestFitting`]: FormatElement::BestFitting pub(crate) fn propagate_expand(&mut self) { #[derive(Debug)] enum Enclosing<'a> { From f604c39d90a4ebffbbdb9a09d620a5dbee76bbdd Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 28 Sep 2022 17:13:26 +0200 Subject: [PATCH 5/5] feat(rome_js_formatter): Format Member Chain (#3273) Co-authored-by: Emanuele Stoppa --- .../expressions/arrow_function_expression.rs | 2 +- .../src/js/expressions/call_arguments.rs | 193 +++-- .../src/js/expressions/call_expression.rs | 49 +- .../expressions/computed_member_expression.rs | 32 +- .../expressions/static_member_expression.rs | 32 +- .../expressions/type_assertion_expression.rs | 53 +- .../src/utils/assignment_like.rs | 8 +- .../src/utils/member_chain/chain_member.rs | 221 +++--- .../src/utils/member_chain/groups.rs | 302 ++++--- .../src/utils/member_chain/mod.rs | 748 ++++++++++++------ .../src/utils/member_chain/simple_argument.rs | 97 ++- crates/rome_js_formatter/src/utils/mod.rs | 3 +- .../declarations/variable_declaration.js.snap | 114 ++- .../expression/member-chain/computed.js.snap | 3 +- .../static_member_expression.js.snap | 28 +- .../js/assignment/call-with-template.js.snap | 56 -- .../prettier/js/assignment/lone-arg.js.snap | 77 -- .../closure-compiler-type-cast.js.snap | 190 ----- .../comment-in-the-middle.js.snap | 57 -- .../functional_compose.js.snap | 20 +- .../functional-composition/rxjs_pipe.js.snap | 61 -- .../specs/prettier/js/member/expand.js.snap | 117 --- .../prettier/js/method-chain/13018.js.snap | 69 -- .../js/method-chain/bracket_0-1.js.snap | 34 - .../prettier/js/method-chain/comment.js.snap | 160 ---- .../js/method-chain/computed-merge.js.snap | 65 -- .../prettier/js/method-chain/computed.js.snap | 43 - .../js/method-chain/conditional.js.snap | 95 --- .../specs/prettier/js/method-chain/d3.js.snap | 87 -- .../js/method-chain/first_long.js.snap | 53 +- .../method-chain/fluent-configuration.js.snap | 63 -- .../js/method-chain/issue-3594.js.snap | 53 -- .../js/method-chain/issue-4125.js.snap | 124 +-- .../prettier/js/method-chain/logical.js.snap | 36 +- .../js/method-chain/multiple-members.js.snap | 133 ---- .../prettier/js/method-chain/square_0.js.snap | 52 -- .../prettier/js/method-chain/test.js.snap | 15 +- .../prettier/js/method-chain/this.js.snap | 37 - .../js/multiparser-css/issue-11797.js.snap | 2 + .../js/optional-chaining/comments.js.snap | 148 ---- .../js/preserve-line/member-chain.js.snap | 191 ----- .../specs/prettier/js/require/require.js.snap | 75 -- .../styled-jsx-with-expressions.js.snap | 2 + .../specs/prettier/js/template/call.js.snap | 73 -- .../specs/prettier/js/template/inline.js.snap | 99 --- .../js/ternaries/indent-after-paren.js.snap | 675 ---------------- .../typescript/cast/generic-cast.ts.snap | 321 -------- .../custom/typeParameters/variables.ts.snap | 87 -- .../typescript/ternaries/indent.ts.snap | 160 ---- .../typeparams/class-method.ts.snap | 55 +- editors/vscode/src/commands/syntaxTree.ts | 18 +- npm/backend-jsonrpc/src/command.ts | 2 +- npm/backend-jsonrpc/src/index.ts | 10 +- npm/backend-jsonrpc/src/socket.ts | 6 +- npm/backend-jsonrpc/src/transport.ts | 20 +- npm/backend-jsonrpc/tests/transport.test.mjs | 74 +- npm/backend-jsonrpc/tests/workspace.test.mjs | 10 +- website/.eleventy.js | 17 +- 58 files changed, 1334 insertions(+), 4293 deletions(-) delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/assignment/call-with-template.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/assignment/lone-arg.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/closure-compiler-type-cast.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-in-the-middle.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/rxjs_pipe.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/member/expand.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/13018.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/bracket_0-1.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/comment.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed-merge.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/conditional.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/d3.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/fluent-configuration.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-3594.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/multiple-members.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/square_0.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/method-chain/this.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/optional-chaining/comments.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/preserve-line/member-chain.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/require/require.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/template/call.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/template/inline.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/js/ternaries/indent-after-paren.js.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/typescript/cast/generic-cast.ts.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/typescript/custom/typeParameters/variables.ts.snap delete mode 100644 crates/rome_js_formatter/tests/specs/prettier/typescript/ternaries/indent.ts.snap diff --git a/crates/rome_js_formatter/src/js/expressions/arrow_function_expression.rs b/crates/rome_js_formatter/src/js/expressions/arrow_function_expression.rs index 09dfd0afc87..27d633b36e4 100644 --- a/crates/rome_js_formatter/src/js/expressions/arrow_function_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/arrow_function_expression.rs @@ -544,7 +544,7 @@ fn template_literal_contains_new_line(template: &JsTemplate) -> bool { /// ``` /// /// Returns `false` because the template isn't on the same line as the '+' token. -fn is_multiline_template_starting_on_same_line(template: &JsTemplate) -> bool { +pub(crate) fn is_multiline_template_starting_on_same_line(template: &JsTemplate) -> bool { let contains_new_line = template_literal_contains_new_line(template); let starts_on_same_line = template.syntax().first_token().map_or(false, |token| { diff --git a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs index ab166b2be4f..f6dcb0f4bc4 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs @@ -1,10 +1,12 @@ +use crate::js::expressions::arrow_function_expression::is_multiline_template_starting_on_same_line; use crate::prelude::*; use crate::utils::{is_call_like_expression, write_arguments_multi_line}; use rome_formatter::{format_args, write, CstFormatContext}; use rome_js_syntax::{ JsAnyCallArgument, JsAnyExpression, JsAnyFunctionBody, JsAnyLiteralExpression, JsAnyName, JsAnyStatement, JsArrayExpression, JsArrowFunctionExpression, JsCallArgumentList, - JsCallArguments, JsCallArgumentsFields, JsCallExpression, TsReferenceType, + JsCallArguments, JsCallArgumentsFields, JsCallExpression, JsExpressionStatement, + TsReferenceType, }; use rome_rowan::{AstSeparatedList, SyntaxResult, SyntaxTokenText}; @@ -33,6 +35,28 @@ impl FormatNodeRule for FormatJsCallArguments { ); } + let call_expression = node.parent::(); + + if is_commonjs_or_amd_call(node, call_expression.as_ref())? + || is_multiline_template_only_args(node) + { + return write!( + f, + [ + l_paren_token.format(), + format_with(|f| { + f.join_with(space()) + .entries( + args.format_separated(",") + .with_trailing_separator(TrailingSeparator::Omit), + ) + .finish() + }), + r_paren_token.format() + ] + ); + } + let mut iter = args.iter(); let first_argument = iter.next(); let second_argument = iter.next(); @@ -97,19 +121,29 @@ impl FormatNodeRule for FormatJsCallArguments { .map(|e| e.memoized()) .collect(); - let an_argument_breaks = - separated - .iter_mut() - .enumerate() - .any(|(index, element)| match element.inspect(f) { - Ok(element) => { - let in_relevant_range = should_group_first_argument && index > 0 - || (should_group_last_argument && index < args.len() - 1); + let mut any_argument_breaks = false; + let mut first_last_breaks = false; - in_relevant_range && element.will_break() - } - Err(_) => false, - }); + for (index, argument) in separated.iter_mut().enumerate() { + let breaks = argument.inspect(f)?.will_break(); + + any_argument_breaks = any_argument_breaks || breaks; + + if (should_group_first_argument && index > 0) + || (should_group_last_argument && index < args.len() - 1) + { + first_last_breaks = first_last_breaks || breaks; + if breaks { + break; + } + } + } + + let format_flat_arguments = format_with(|f| { + f.join_with(soft_line_break_or_space()) + .entries(separated.iter()) + .finish() + }); // We now cache them the delimiters tokens. This is needed because `[rome_formatter::best_fitting]` will try to // print each version first @@ -125,27 +159,18 @@ impl FormatNodeRule for FormatJsCallArguments { // function, but here we use a different way to print the trailing separator write!( f, - [ - &l_paren, - &group(&format_with(|f| { - write!( - f, - [ - &soft_block_indent(&format_args![ - format_with(|f| { - write_arguments_multi_line(separated.iter(), f) - }), - soft_line_break() - ]), - &r_paren - ] - ) + [group(&format_args![ + l_paren, + soft_block_indent(&format_with(|f| { + write_arguments_multi_line(separated.iter(), f) })), - ] + r_paren + ]) + .should_expand(true)] ) }); - if an_argument_breaks { + if first_last_breaks { return write!(f, [all_arguments_expanded]); } @@ -160,39 +185,26 @@ impl FormatNodeRule for FormatJsCallArguments { let mut iter = separated.iter(); // SAFETY: check on the existence of at least one argument are done before let first = iter.next().unwrap(); - f.join_with(&space()) - .entry(&format_with(|f| { - write!(f, [&format_args![first, expand_parent()]]) - })) - .entries(iter) - .finish()?; + f.join_with(&space()).entry(&first).entries(iter).finish()?; } else { // special formatting of the last element let mut iter = separated.iter(); // SAFETY: check on the existence of at least one argument are done before let last = iter.next_back().unwrap(); - - f.join_with(&space()) - .entries(iter) - .entry(&format_with(|f| { - write!(f, [&format_args![last, expand_parent()]]) - })) - .finish()?; + f.join_with(&space()).entries(iter).entry(&last).finish()?; } write!(f, [r_paren]) }); + if any_argument_breaks { + write!(f, [expand_parent()])?; + } + write!( f, [best_fitting![ - format_args![ - l_paren, - group(&format_with(|f| { - write_arguments_multi_line(separated.iter(), f) - })), - r_paren, - ], - edge_arguments_do_not_break, + format_args![l_paren, format_flat_arguments, r_paren], + group(&edge_arguments_do_not_break).should_expand(true), all_arguments_expanded ]] ) @@ -209,7 +221,7 @@ impl FormatNodeRule for FormatJsCallArguments { write_arguments_multi_line(separated, f) })), r_paren, - ]),] + ])] ) } } @@ -412,6 +424,83 @@ fn could_group_expression_argument( Ok(result) } +/// Tests if this is a call to commonjs [`require`](https://nodejs.org/api/modules.html#requireid) +/// or amd's [`define`](https://github.com/amdjs/amdjs-api/wiki/AMD#define-function-) function. +fn is_commonjs_or_amd_call( + arguments: &JsCallArguments, + call: Option<&JsCallExpression>, +) -> SyntaxResult { + let call = match call { + Some(call) => call, + None => return Ok(false), + }; + + let callee = call.callee()?; + + Ok(match callee { + JsAnyExpression::JsIdentifierExpression(identifier) => { + let reference = identifier.name()?; + + if reference.has_name("require") { + true + } else if reference.has_name("define") { + let in_statement = call.parent::().is_some(); + + if in_statement { + let args = arguments.args(); + match args.len() { + 1 => true, + 2 => matches!( + args.first(), + Some(Ok(JsAnyCallArgument::JsAnyExpression( + JsAnyExpression::JsArrayExpression(_) + ))) + ), + 3 => { + let mut iter = args.iter(); + let first = iter.next(); + let second = iter.next(); + matches!( + (first, second), + ( + Some(Ok(JsAnyCallArgument::JsAnyExpression( + JsAnyExpression::JsAnyLiteralExpression( + JsAnyLiteralExpression::JsStringLiteralExpression(_) + ) + ))), + Some(Ok(JsAnyCallArgument::JsAnyExpression( + JsAnyExpression::JsArrayExpression(_) + ))) + ) + ) + } + _ => false, + } + } else { + false + } + } else { + false + } + } + _ => false, + }) +} + +/// Returns `true` if `arguments` contains a single [multiline template literal argument that starts on its own ](is_multiline_template_starting_on_same_line). +fn is_multiline_template_only_args(arguments: &JsCallArguments) -> bool { + let args = arguments.args(); + + match args.first() { + Some(Ok(JsAnyCallArgument::JsAnyExpression(JsAnyExpression::JsTemplate(template)))) + if args.len() == 1 => + { + is_multiline_template_starting_on_same_line(&template) + } + _ => false, + } +} + /// This function is used to check if the code is a hook-like code: /// /// ```js diff --git a/crates/rome_js_formatter/src/js/expressions/call_expression.rs b/crates/rome_js_formatter/src/js/expressions/call_expression.rs index 3d1d3560d68..ebf7c54b1a8 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_expression.rs @@ -1,17 +1,58 @@ use crate::prelude::*; +use rome_formatter::write; use crate::parentheses::NeedsParentheses; -use crate::utils::get_member_chain; -use rome_js_syntax::{JsCallExpression, JsSyntaxKind, JsSyntaxNode}; +use crate::utils::member_chain::MemberChain; +use rome_js_syntax::{ + JsAnyExpression, JsCallExpression, JsCallExpressionFields, JsSyntaxKind, JsSyntaxNode, +}; #[derive(Debug, Clone, Default)] pub struct FormatJsCallExpression; impl FormatNodeRule for FormatJsCallExpression { fn fmt_fields(&self, node: &JsCallExpression, f: &mut JsFormatter) -> FormatResult<()> { - let member_chain = get_member_chain(node, f)?; + let JsCallExpressionFields { + callee, + optional_chain_token, + type_arguments, + arguments, + } = node.as_fields(); - member_chain.fmt(f) + let callee = callee?; + + if matches!( + callee, + JsAnyExpression::JsStaticMemberExpression(_) + | JsAnyExpression::JsComputedMemberExpression(_) + ) && !callee.needs_parentheses() + { + let member_chain = MemberChain::from_call_expression( + node.clone(), + f.comments(), + f.options().tab_width(), + )?; + + member_chain.fmt(f) + } else { + let format_inner = format_with(|f| { + write!( + f, + [ + callee.format(), + optional_chain_token.format(), + type_arguments.format(), + arguments.format() + ] + ) + }); + + if matches!(callee, JsAnyExpression::JsCallExpression(_)) { + write!(f, [group(&format_inner)]) + } else { + write!(f, [format_inner]) + } + } } fn needs_parentheses(&self, item: &JsCallExpression) -> bool { diff --git a/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs b/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs index 649f6ee37ba..fa8dbe8804c 100644 --- a/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs @@ -34,17 +34,32 @@ impl Format for JsAnyComputedMemberLike { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { write!(f, [self.object().format()])?; - match self.member()? { + FormatComputedMemberLookup(self).fmt(f) + } +} + +/// Formats the lookup portion (everything except the object) of a computed member like. +pub(crate) struct FormatComputedMemberLookup<'a>(&'a JsAnyComputedMemberLike); + +impl<'a> FormatComputedMemberLookup<'a> { + pub(crate) fn new(member_like: &'a JsAnyComputedMemberLike) -> Self { + Self(member_like) + } +} + +impl Format for FormatComputedMemberLookup<'_> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + match self.0.member()? { JsAnyExpression::JsAnyLiteralExpression( JsAnyLiteralExpression::JsNumberLiteralExpression(literal), ) => { write!( f, [ - self.optional_chain_token().format(), - self.l_brack_token().format(), + self.0.optional_chain_token().format(), + self.0.l_brack_token().format(), literal.format(), - self.r_brack_token().format() + self.0.r_brack_token().format() ] ) } @@ -52,12 +67,11 @@ impl Format for JsAnyComputedMemberLike { write![ f, [group(&format_args![ - self.optional_chain_token().format(), - self.l_brack_token().format(), - soft_line_break(), + self.0.optional_chain_token().format(), + self.0.l_brack_token().format(), soft_block_indent(&member.format()), - self.r_brack_token().format() - ]),] + self.0.r_brack_token().format() + ])] ] } } diff --git a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs index 8110a73eec7..c02f95ced9a 100644 --- a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs @@ -2,6 +2,7 @@ use crate::prelude::*; use crate::js::expressions::computed_member_expression::JsAnyComputedMemberLike; use crate::parentheses::NeedsParentheses; +use crate::utils::member_chain::MemberChainLabel; use rome_formatter::{format_args, write}; use rome_js_syntax::{ JsAnyAssignment, JsAnyAssignmentPattern, JsAnyExpression, JsAnyName, JsAssignmentExpression, @@ -38,13 +39,34 @@ declare_node_union! { impl Format for JsAnyStaticMemberLike { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - write!(f, [self.object().format()])?; + let is_member_chain = { + let mut recording = f.start_recording(); + write!(recording, [self.object().format()])?; - let layout = self.layout()?; + recording + .stop() + .has_label(LabelId::of::()) + }; + + let layout = self.layout(is_member_chain)?; match layout { StaticMemberLikeLayout::NoBreak => { - write!(f, [self.operator_token().format(), self.member().format()]) + let format_no_break = format_with(|f| { + write!(f, [self.operator_token().format(), self.member().format()]) + }); + + if is_member_chain { + write!( + f, + [labelled( + LabelId::of::(), + &format_no_break + )] + ) + } else { + write!(f, [format_no_break]) + } } StaticMemberLikeLayout::BreakAfterObject => { write!( @@ -84,7 +106,7 @@ impl JsAnyStaticMemberLike { } } - fn layout(&self) -> SyntaxResult { + fn layout(&self, is_member_chain: bool) -> SyntaxResult { let parent = self.syntax().parent(); let object = self.object()?; @@ -108,7 +130,7 @@ impl JsAnyStaticMemberLike { _ => false, }; - if no_break { + if no_break || is_member_chain { return Ok(StaticMemberLikeLayout::NoBreak); } } diff --git a/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs b/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs index 254da605f18..e0f9e2bdbee 100644 --- a/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use crate::parentheses::{is_callee, is_member_object, is_spread, is_tag, NeedsParentheses}; -use rome_formatter::write; -use rome_js_syntax::JsSyntaxNode; +use rome_formatter::{format_args, write}; +use rome_js_syntax::{JsAnyExpression, JsSyntaxNode}; use rome_js_syntax::{JsSyntaxKind, TsTypeAssertionExpression, TsTypeAssertionExpressionFields}; #[derive(Debug, Clone, Default)] @@ -21,15 +21,46 @@ impl FormatNodeRule for FormatTsTypeAssertionExpressi expression, } = node.as_fields(); - write![ - f, - [ - l_angle_token.format(), - group(&soft_block_indent(&ty.format())), - r_angle_token.format(), - expression.format() - ] - ] + let expression = expression?; + + let break_after_cast = !matches!( + expression, + JsAnyExpression::JsArrayExpression(_) | JsAnyExpression::JsObjectExpression(_) + ); + + let format_cast = format_with(|f| { + write!( + f, + [ + l_angle_token.format(), + group(&soft_block_indent(&ty.format())), + r_angle_token.format(), + ] + ) + }); + + if break_after_cast { + let format_cast = format_cast.memoized(); + let format_expression = expression.format().memoized(); + + write!( + f, + [best_fitting![ + format_args![format_cast, format_expression], + format_args![ + format_cast, + group(&format_args![ + text("("), + block_indent(&format_expression), + text(")") + ]) + ], + format_args![format_cast, format_expression] + ]] + ) + } else { + write![f, [format_cast, expression.format()]] + } } fn needs_parentheses(&self, item: &TsTypeAssertionExpression) -> bool { diff --git a/crates/rome_js_formatter/src/utils/assignment_like.rs b/crates/rome_js_formatter/src/utils/assignment_like.rs index 50e50699c99..d580182598d 100644 --- a/crates/rome_js_formatter/src/utils/assignment_like.rs +++ b/crates/rome_js_formatter/src/utils/assignment_like.rs @@ -992,7 +992,7 @@ impl Format for JsAnyAssignmentLike { /// [Prettier applies]: https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/assignment.js#L329 fn is_poorly_breakable_member_or_call_chain( expression: &JsAnyExpression, - f: &mut Formatter, + f: &Formatter, ) -> SyntaxResult { let threshold = f.options().line_width().value() / 4; @@ -1041,7 +1041,11 @@ fn is_poorly_breakable_member_or_call_chain( } for call_expression in call_expressions { - if is_member_call_chain(&call_expression, f)? { + if is_member_call_chain( + call_expression.clone(), + f.comments(), + f.options().tab_width(), + )? { return Ok(false); } diff --git a/crates/rome_js_formatter/src/utils/member_chain/chain_member.rs b/crates/rome_js_formatter/src/utils/member_chain/chain_member.rs index 3d74b494279..23bcafc9d1d 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/chain_member.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/chain_member.rs @@ -1,34 +1,51 @@ -use crate::context::TabWidth; +use crate::js::expressions::computed_member_expression::FormatComputedMemberLookup; use crate::prelude::*; use rome_formatter::write; use rome_js_syntax::{ - JsAnyExpression, JsCallExpression, JsCallExpressionFields, JsComputedMemberExpression, - JsComputedMemberExpressionFields, JsIdentifierExpression, JsImportCallExpression, - JsNewExpression, JsStaticMemberExpression, JsStaticMemberExpressionFields, JsSyntaxNode, - JsThisExpression, + JsCallExpression, JsCallExpressionFields, JsComputedMemberExpression, JsImportCallExpression, + JsStaticMemberExpression, JsStaticMemberExpressionFields, JsSyntaxNode, + TsNonNullAssertionExpression, TsNonNullAssertionExpressionFields, }; -use rome_rowan::{AstNode, SyntaxResult}; +use rome_rowan::AstNode; use std::fmt::Debug; +#[derive(Copy, Clone, Debug)] +pub(crate) enum CallExpressionPosition { + /// At the start of a call chain. + /// `of` in `of().test` + Start, + + /// Somewhere in the middle. + /// + /// `b` in `a.b().c()` + Middle, + + /// At the end of a call chain (root) + /// `c` in `a.b.c()` + End, +} + /// Data structure that holds the node with its formatted version #[derive(Clone, Debug)] pub(crate) enum ChainMember { /// Holds onto a [rome_js_syntax::JsStaticMemberExpression] StaticMember { expression: JsStaticMemberExpression, - root: bool, }, /// Holds onto a [rome_js_syntax::JsCallExpression] CallExpression { expression: JsCallExpression, - root: bool, + position: CallExpressionPosition, }, /// Holds onto a [rome_js_syntax::JsComputedMemberExpression] ComputedMember { expression: JsComputedMemberExpression, - root: bool, + }, + + TsNonNullAssertionExpression { + expression: TsNonNullAssertionExpression, }, /// Any other node that are not [rome_js_syntax::JsCallExpression] or [rome_js_syntax::JsStaticMemberExpression] @@ -37,23 +54,28 @@ pub(crate) enum ChainMember { } impl ChainMember { - /// checks if the current node is a [rome_js_syntax::JsCallExpression], [rome_js_syntax::JsImportExpression] or a [rome_js_syntax::JsNewExpression] - pub fn is_loose_call_expression(&self) -> bool { + /// checks if the current node is a [rome_js_syntax::JsCallExpression], or a [rome_js_syntax::JsImportExpression] + pub fn is_call_like_expression(&self) -> bool { match self { ChainMember::CallExpression { .. } => true, ChainMember::Node(node) => { JsImportCallExpression::can_cast(node.kind()) - | JsNewExpression::can_cast(node.kind()) + | JsCallExpression::can_cast(node.kind()) } _ => false, } } + pub(crate) const fn is_call_expression(&self) -> bool { + matches!(self, ChainMember::CallExpression { .. }) + } + pub(crate) fn syntax(&self) -> &JsSyntaxNode { match self { ChainMember::StaticMember { expression, .. } => expression.syntax(), ChainMember::CallExpression { expression, .. } => expression.syntax(), ChainMember::ComputedMember { expression, .. } => expression.syntax(), + ChainMember::TsNonNullAssertionExpression { expression } => expression.syntax(), ChainMember::Node(node) => node, } } @@ -62,147 +84,120 @@ impl ChainMember { matches!(self, ChainMember::ComputedMember { .. }) } - pub(crate) fn is_this_expression(&self) -> bool { + pub(super) fn needs_empty_line_before(&self) -> bool { match self { - ChainMember::Node(node) => JsThisExpression::can_cast(node.kind()), - _ => false, - } - } + ChainMember::StaticMember { expression } => { + let operator = expression.operator_token(); - pub(crate) fn is_identifier_expression(&self) -> bool { - match self { - ChainMember::Node(node) => JsIdentifierExpression::can_cast(node.kind()), - _ => false, - } - } - - /// There are cases like Object.keys(), Observable.of(), _.values() where - /// they are the subject of all the chained calls and therefore should - /// be kept on the same line: - /// - /// ```js - /// Object.keys(items) - /// .filter(x => x) - /// .map(x => x) - /// ``` - /// In order to detect those cases, we use an heuristic: if the first - /// node is an identifier with the name starting with a capital - /// letter or just a sequence of _$. The rationale is that they are - /// likely to be factories. - /// - /// Comment from [Prettier] - /// - /// [Prettier]: https://github.com/prettier/prettier/blob/main/src/language-js/print/member-chain.js#L252-L266 - pub(crate) fn is_factory(&self, check_left_hand_side: bool) -> SyntaxResult { - fn check_str(text: &str) -> bool { - text.chars().next().map_or(false, |c| c.is_uppercase()) - || text.starts_with('_') - || text.starts_with('$') - } - - if let ChainMember::StaticMember { expression, .. } = self { - if check_left_hand_side { - if let JsAnyExpression::JsIdentifierExpression(identifier_expression) = - expression.object()? - { - let value_token = identifier_expression.name()?.value_token()?; - let text = value_token.text_trimmed(); - Ok(check_str(text)) - } else { - Ok(false) + match operator { + Ok(operator) => get_lines_before_token(&operator) > 1, + _ => false, } - } else { - Ok(check_str(expression.member()?.text().as_str())) } - } else if let ChainMember::Node(node, ..) = self { - if let Some(identifier_expression) = JsIdentifierExpression::cast(node.clone()) { - let value_token = identifier_expression.name()?.value_token()?; - let text = value_token.text_trimmed(); - Ok(check_str(text)) - } else { - Ok(false) - } - } else { - Ok(false) - } - } + ChainMember::ComputedMember { expression } => { + let l_brack_token = expression.l_brack_token(); - pub(crate) fn has_short_name(&self, tab_width: TabWidth) -> SyntaxResult { - if let ChainMember::StaticMember { expression, .. } = self { - if let JsAnyExpression::JsIdentifierExpression(identifier_expression) = - expression.object()? - { - let value_token = identifier_expression.name()?.value_token()?; - let text = value_token.text_trimmed(); - Ok(text.len() <= u8::from(tab_width) as usize) - } else { - Ok(false) + match l_brack_token { + Ok(l_brack_token) => { + get_lines_before_token( + &expression.optional_chain_token().unwrap_or(l_brack_token), + ) > 1 + } + _ => false, + } } - } else { - Ok(false) + _ => false, } } } impl Format for ChainMember { fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> { + if self.needs_empty_line_before() { + write!(f, [empty_line()])?; + } + match self { - ChainMember::StaticMember { expression, root } => { + ChainMember::StaticMember { expression } => { let JsStaticMemberExpressionFields { // Formatted as part of the previous item object: _, operator_token, member, } = expression.as_fields(); - write![ + + write!( f, [ - (!root).then_some(format_leading_comments(expression.syntax())), + format_leading_comments(expression.syntax()), operator_token.format(), member.format(), - (!root).then_some(format_trailing_comments(expression.syntax())) + format_trailing_comments(expression.syntax()) ] - ] + ) } - ChainMember::CallExpression { expression, root } => { - let JsCallExpressionFields { - // Formatted as part of the previous item - callee: _, - optional_chain_token, - type_arguments, - arguments, + ChainMember::TsNonNullAssertionExpression { expression } => { + let TsNonNullAssertionExpressionFields { + expression: _, + excl_token, } = expression.as_fields(); write!( f, [ - (!root).then_some(format_leading_comments(expression.syntax())), - optional_chain_token.format(), - type_arguments.format(), - arguments.format(), - (!root).then_some(format_trailing_comments(expression.syntax())) + format_leading_comments(expression.syntax()), + excl_token.format(), + format_trailing_comments(expression.syntax()) ] ) } - ChainMember::ComputedMember { expression, root } => { - let JsComputedMemberExpressionFields { + + ChainMember::CallExpression { + expression, + position, + } => { + let JsCallExpressionFields { // Formatted as part of the previous item - object: _, + callee: _, optional_chain_token, - l_brack_token, - member, - r_brack_token, + type_arguments, + arguments, } = expression.as_fields(); + + match position { + CallExpressionPosition::Start => write!(f, [expression.format()]), + CallExpressionPosition::Middle => { + write!( + f, + [ + format_leading_comments(expression.syntax()), + optional_chain_token.format(), + type_arguments.format(), + arguments.format(), + format_trailing_comments(expression.syntax()) + ] + ) + } + CallExpressionPosition::End => { + write!( + f, + [ + optional_chain_token.format(), + type_arguments.format(), + arguments.format(), + ] + ) + } + } + } + ChainMember::ComputedMember { expression } => { write!( f, [ - (!root).then_some(format_leading_comments(expression.syntax())), - optional_chain_token.format(), - l_brack_token.format(), - member.format(), - r_brack_token.format(), - (!root).then_some(format_trailing_comments(expression.syntax())) + format_leading_comments(expression.syntax()), + FormatComputedMemberLookup::new(&expression.clone().into()), + format_trailing_comments(expression.syntax()) ] ) } diff --git a/crates/rome_js_formatter/src/utils/member_chain/groups.rs b/crates/rome_js_formatter/src/utils/member_chain/groups.rs index 7dcf3479008..3e38ef09987 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/groups.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/groups.rs @@ -1,114 +1,103 @@ -use crate::context::TabWidth; use crate::parentheses::NeedsParentheses; use crate::prelude::*; use crate::utils::member_chain::chain_member::ChainMember; use rome_formatter::write; -use rome_js_syntax::JsCallExpression; -use rome_rowan::SyntaxResult; -use std::mem; +use std::cell::RefCell; +#[derive(Default)] pub(super) struct MemberChainGroupsBuilder { /// keeps track of the groups created groups: Vec, /// keeps track of the current group that is being created/updated - current_group: MemberChainGroup, - - /// If the current group is inside an expression statement. - /// - /// This information is important when evaluating the break of the groups. - in_expression_statement: bool, - - tab_width: TabWidth, + current_group: Option, } impl MemberChainGroupsBuilder { - pub fn new(in_expression_statement: bool, tab_width: TabWidth) -> Self { - Self { - in_expression_statement, - groups: Vec::new(), - current_group: MemberChainGroup::default(), - tab_width, - } - } - /// starts a new group - pub fn start_group(&mut self, flatten_item: ChainMember) { - debug_assert!(self.current_group.members.is_empty()); - self.current_group.members.push(flatten_item); + pub fn start_group(&mut self, member: ChainMember) { + debug_assert!(self.current_group.is_none()); + let mut group = MemberChainGroup::default(); + group.members.push(member); + self.current_group = Some(group); } /// continues of starts a new group - pub fn start_or_continue_group(&mut self, flatten_item: ChainMember) { - if self.current_group.members.is_empty() { - self.start_group(flatten_item); - } else { - self.continue_group(flatten_item); + pub fn start_or_continue_group(&mut self, member: ChainMember) { + match &mut self.current_group { + None => self.start_group(member), + Some(group) => group.members.push(member), } } - /// adds the passed element to the current group - pub fn continue_group(&mut self, flatten_item: ChainMember) { - debug_assert!(!self.current_group.members.is_empty()); - self.current_group.members.push(flatten_item); + /// adds the passed element to the current group. + /// + /// # Panics + /// + /// If there's no started group. + pub fn continue_group(&mut self, member: ChainMember) { + match &mut self.current_group { + None => { + panic!("It is necessary to start a group first using `start_group`."); + } + Some(group) => { + group.members.push(member); + } + } } - /// clears the current group, and adds a new group to the groups + /// clears the current group, and adds it to the groups collection pub fn close_group(&mut self) { - if !self.current_group.members.is_empty() { - let mut elements = MemberChainGroup::default(); - std::mem::swap(&mut elements, &mut self.current_group); - self.groups.push(elements); + if let Some(group) = self.current_group.take() { + self.groups.push(group); } } - pub(super) fn finish(self) -> MemberChainGroups { - debug_assert!(self.current_group.members().is_empty()); + pub(super) fn finish(self) -> TailChainGroups { + let mut groups = self.groups; - MemberChainGroups { - groups: self.groups, - tab_width: self.tab_width, - in_expression_statement: self.in_expression_statement, - cutoff: 1, + if let Some(group) = self.current_group { + groups.push(group); } + + TailChainGroups { groups } } } +/// Groups following on the head group. +/// +/// May be empty if all members are part of the head group #[derive(Clone, Debug)] -/// Handles creation of groups while scanning the flatten items -pub(super) struct MemberChainGroups { - /// keeps track of the groups created +pub(super) struct TailChainGroups { groups: Vec, - - /// If the current group is inside an expression statement. - /// - /// This information is important when evaluating the break of the groups. - in_expression_statement: bool, - - tab_width: TabWidth, - - /// This is a threshold of when we should start breaking the groups - /// - /// By default, it's 1, meaning that we start breaking after the first group. - cutoff: u8, } -impl MemberChainGroups { +impl TailChainGroups { + /// Returns `true` if there are no tail groups. pub(crate) fn is_empty(&self) -> bool { self.groups.is_empty() } - /// This function checks if the current grouping should be merged with the first group. - pub fn should_merge( - &self, - head_group: &MemberChainGroup, - comments: &JsComments, - ) -> SyntaxResult { - Ok(!self.groups.len() >= 1 - && self.should_not_wrap(head_group)? - && !self.groups[0] - .members - .first() - .map_or(false, |item| comments.has_comments(item.syntax()))) + /// Returns the number of tail groups. + pub(crate) fn len(&self) -> usize { + self.groups.len() + } + + /// Returns the first group + pub(crate) fn first(&self) -> Option<&MemberChainGroup> { + self.groups.first() + } + + /// Returns the last group + pub(crate) fn last(&self) -> Option<&MemberChainGroup> { + self.groups.last() + } + + /// Removes the first group and returns it + pub(super) fn pop_first(&mut self) -> Option { + match self.groups.len() { + 0 => None, + _ => Some(self.groups.remove(0)), + } } /// Checks if the groups contain comments. @@ -120,8 +109,8 @@ impl MemberChainGroups { || comments.has_leading_comments(item.syntax()) }); - let cutoff_has_leading_comments = if self.groups.len() >= self.cutoff as usize { - let group = self.groups.get(self.cutoff as usize); + let cutoff_has_leading_comments = if !self.groups.is_empty() { + let group = self.groups.get(1); if let Some(group) = group { let first_item = group.members.first(); first_item.map_or(false, |first_item| { @@ -137,99 +126,50 @@ impl MemberChainGroups { has_comments || cutoff_has_leading_comments } - /// Filters the stack of [FlattenItem] and return only the ones that - /// contain [JsCallExpression]. The function returns the actual nodes. - pub fn get_call_expressions(&self) -> impl Iterator { - self.groups - .iter() - .flat_map(|group| group.members.iter()) - .filter_map(|item| { - if let ChainMember::CallExpression { expression, .. } = item { - Some(expression) - } else { - None - } - }) + /// Here we check if the length of the groups exceeds the cutoff or there are comments + /// This function is the inverse of the prettier function + /// [Prettier applies]: https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/member-chain.js#L342 + pub(crate) fn is_member_call_chain(&self, comments: &JsComments) -> bool { + self.groups.len() > 1 || self.has_comments(comments) } - /// This is an heuristic needed to check when the first element of the group - /// Should be part of the "head" or the "tail". - fn should_not_wrap(&self, first_group: &MemberChainGroup) -> SyntaxResult { - let tab_with = self.tab_width; - let has_computed_property = if self.groups.len() > 1 { - // SAFETY: guarded by the previous check - let group = &self.groups[0]; - group - .members - .first() - .map_or(false, |item| item.is_computed_expression()) - } else { - false - }; - - if first_group.members().len() == 1 { - // SAFETY: access is guarded by the previous check - let first_node = first_group.members().first().unwrap(); - - return Ok(first_node.is_this_expression() - || (first_node.is_identifier_expression() - && (first_node.is_factory(true)? - // If an identifier has a name that is shorter than the tab with, then we join it with the "head" - || (self.in_expression_statement - && first_node.has_short_name(tab_with)?) - || has_computed_property))); - } - - let last_node_is_factory = self - .groups - .iter() - .flat_map(|group| group.members.iter()) - .last() - .map_or(false, |item| item.is_factory(false).unwrap_or(false)); - - Ok(last_node_is_factory || has_computed_property) + /// Returns an iterator over the groups. + pub(super) fn iter(&self) -> impl Iterator + DoubleEndedIterator { + self.groups.iter() } - /// Here we check if the first group can be merged to the head. If so, then - /// we move out the first group out of the groups - pub(crate) fn should_merge_with_first_group( - &mut self, - head_group: &MemberChainGroup, - comments: &JsComments, - ) -> Option> { - if self.should_merge(head_group, comments).unwrap_or(false) { - let mut new_groups = self.groups.split_off(1); - // self.groups is now the head (one element), while `new_groups` is a new vector without the - // first element. - // As we need to achieve the opposite, we now swap them. - mem::swap(&mut self.groups, &mut new_groups); - Some(new_groups) - } else { - None + /// Test if any group except the last group [break](FormatElements::will_break). + pub(super) fn any_except_last_will_break(&self, f: &mut JsFormatter) -> FormatResult { + for group in &self.groups[..self.groups.len().saturating_sub(1)] { + if group.will_break(f)? { + return Ok(true); + } } - } - /// Here we check if the length of the groups exceeds the cutoff or there are comments - /// This function is the inverse of the prettier function - /// [Prettier applies]: https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/member-chain.js#L342 - pub(crate) fn is_member_call_chain(&self, comments: &JsComments) -> bool { - self.groups.len() > self.cutoff as usize || self.has_comments(comments) + Ok(false) } - pub(super) fn iter(&self) -> impl Iterator { - self.groups.iter() + /// Returns an iterator over all members + pub(super) fn members(&self) -> impl Iterator + DoubleEndedIterator { + self.groups.iter().flat_map(|group| group.members().iter()) } } -impl Format for MemberChainGroups { +impl Format for TailChainGroups { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { f.join().entries(self.groups.iter()).finish() } } -#[derive(Debug, Clone, Default)] +#[derive(Clone, Default)] pub(super) struct MemberChainGroup { members: Vec, + + /// Stores the formatted result of this group. + /// + /// Manual implementation of `Memoized` to only memorizing the formatted result + /// if [MemberChainGroup::will_break] is called but not otherwise. + formatted: RefCell>, } impl MemberChainGroup { @@ -237,12 +177,35 @@ impl MemberChainGroup { self.members } - fn members(&self) -> &[ChainMember] { + /// Returns the chain members of the group. + pub(super) fn members(&self) -> &[ChainMember] { &self.members } - pub(super) fn expand_group(&mut self, group: impl IntoIterator) { - self.members.extend(group) + /// Extends the members of this group with the passed in members + pub(super) fn extend_members(&mut self, members: impl IntoIterator) { + self.members.extend(members) + } + + /// Tests if the formatted result of this group results in a [break](FormatElements::will_break). + pub(super) fn will_break(&self, f: &mut JsFormatter) -> FormatResult { + let mut cell = self.formatted.borrow_mut(); + let result = match cell.as_ref() { + Some(formatted) => formatted.will_break(), + None => { + let interned = f.intern(&FormatMemberChainGroup { group: self })?; + + if let Some(interned) = interned { + let breaks = interned.will_break(); + *cell = Some(interned); + breaks + } else { + false + } + } + }; + + Ok(result) } pub(super) fn has_comments(&self, comments: &JsComments) -> bool { @@ -261,13 +224,40 @@ impl MemberChainGroup { impl From> for MemberChainGroup { fn from(entries: Vec) -> Self { - Self { members: entries } + Self { + members: entries, + formatted: RefCell::new(None), + } + } +} + +impl std::fmt::Debug for MemberChainGroup { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("MemberChainGroup") + .field(&self.members) + .finish() } } impl Format for MemberChainGroup { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let last = self.members.last(); + if let Some(formatted) = self.formatted.borrow().as_ref() { + return f.write_element(formatted.clone()); + } + + FormatMemberChainGroup { group: self }.fmt(f) + } +} + +pub struct FormatMemberChainGroup<'a> { + group: &'a MemberChainGroup, +} + +impl Format for FormatMemberChainGroup<'_> { + fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { + let group = self.group; + + let last = group.members.last(); let needs_parens = last.map_or(false, |last| match last { ChainMember::StaticMember { expression, .. } => expression.needs_parentheses(), @@ -275,7 +265,7 @@ impl Format for MemberChainGroup { _ => false, }); - let format_entries = format_with(|f| f.join().entries(self.members.iter()).finish()); + let format_entries = format_with(|f| f.join().entries(group.members.iter()).finish()); if needs_parens { write!(f, [text("("), format_entries, text(")")]) diff --git a/crates/rome_js_formatter/src/utils/member_chain/mod.rs b/crates/rome_js_formatter/src/utils/member_chain/mod.rs index 0d5c0019603..4093a3c66f2 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/mod.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/mod.rs @@ -106,30 +106,153 @@ mod chain_member; mod groups; mod simple_argument; +use crate::context::TabWidth; +use crate::parentheses::is_callee; use crate::prelude::*; -use crate::utils::member_chain::chain_member::ChainMember; +use crate::utils::member_chain::chain_member::{CallExpressionPosition, ChainMember}; use crate::utils::member_chain::groups::{ - MemberChainGroup, MemberChainGroups, MemberChainGroupsBuilder, + MemberChainGroup, MemberChainGroupsBuilder, TailChainGroups, }; use crate::utils::member_chain::simple_argument::SimpleArgument; -use rome_formatter::{format_args, write, Buffer, CstFormatContext}; -use rome_js_syntax::{JsAnyExpression, JsCallExpression, JsExpressionStatement}; +use rome_formatter::{write, Buffer}; +use rome_js_syntax::{ + JsAnyCallArgument, JsAnyExpression, JsAnyLiteralExpression, JsCallExpression, + JsIdentifierExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, JsThisExpression, +}; use rome_rowan::{AstNode, SyntaxResult}; +use std::iter::FusedIterator; + +pub(crate) enum MemberChainLabel {} #[derive(Debug, Clone)] pub(crate) struct MemberChain { - calls_count: usize, + root: JsCallExpression, head: MemberChainGroup, - tail: MemberChainGroups, + tail: TailChainGroups, } impl MemberChain { - /// It tells if the groups should be break on multiple lines - pub(crate) fn groups_should_break(&self, comments: &JsComments) -> FormatResult { - if self.tail.is_empty() { - return Ok(false); + pub(crate) fn from_call_expression( + call_expression: JsCallExpression, + comments: &JsComments, + tab_width: TabWidth, + ) -> SyntaxResult { + let parent = call_expression.syntax().parent(); + let mut chain_members = + ChainMembersIterator::new(call_expression.clone().into(), comments).collect::>(); + chain_members.reverse(); + + // as explained before, the first group is particular, so we calculate it + let (head_group, remaining_members) = + split_members_into_head_and_remaining_groups(chain_members); + + // `flattened_items` now contains only the nodes that should have a sequence of + // `[ StaticMemberExpression -> AnyNode + JsCallExpression ]` + let tail_groups = compute_remaining_groups(remaining_members, comments); + + let mut member_chain = MemberChain { + head: head_group, + tail: tail_groups, + root: call_expression, + }; + + // Here we check if the first element of Groups::groups can be moved inside the head. + // If so, then we extract it and concatenate it together with the head. + member_chain.maybe_merge_with_first_group(comments, tab_width, parent.as_ref()); + + Ok(member_chain) + } + + /// Here we check if the first group can be merged to the head. If so, then + /// we move out the first group out of the groups + fn maybe_merge_with_first_group( + &mut self, + comments: &JsComments, + tab_width: TabWidth, + parent: Option<&JsSyntaxNode>, + ) { + if self.should_merge_tail_with_head(parent, tab_width, comments) { + let group = self.tail.pop_first().unwrap(); + self.head.extend_members(group.into_members()); } + } + /// This function checks if the current grouping should be merged with the first group. + fn should_merge_tail_with_head( + &self, + parent: Option<&JsSyntaxNode>, + tab_width: TabWidth, + comments: &JsComments, + ) -> bool { + let first_group = match self.tail.first() { + None => { + return false; + } + Some(first_group) => first_group, + }; + + let has_comments = first_group + .members() + .first() + .map_or(false, |member| comments.has_comments(member.syntax())); + + if has_comments { + return false; + } + + let has_computed_property = first_group + .members() + .first() + .map_or(false, |item| item.is_computed_expression()); + + if self.head.members().len() == 1 { + let only_member = &self.head.members()[0]; + + let in_expression_statement = parent.map_or(false, |parent| { + parent.kind() == JsSyntaxKind::JS_EXPRESSION_STATEMENT + }); + + match only_member { + ChainMember::Node(node) => { + if JsThisExpression::can_cast(node.kind()) { + true + } else if let Some(identifier) = JsIdentifierExpression::cast_ref(node) { + let is_factory = identifier + .name() + .and_then(|name| name.value_token()) + .as_ref() + .map_or(false, is_factory); + + has_computed_property || + is_factory || + // If an identifier has a name that is shorter than the tab with, then we join it with the "head" + (in_expression_statement + && has_short_name(&identifier, tab_width)) + } else { + false + } + } + _ => false, + } + } else if let Some(ChainMember::StaticMember { expression }) = self.head.members().last() { + let member = expression.member().ok(); + + let is_factory = member + .as_ref() + .and_then(|member| member.as_js_name()) + .and_then(|name| name.value_token().ok()) + .as_ref() + .map_or(false, is_factory); + + has_computed_property || is_factory + } else { + false + } + } + + /// It tells if the groups should break on multiple lines + fn groups_should_break(&self, f: &mut JsFormatter) -> FormatResult { + let comments = f.comments(); let node_has_comments = self.head.has_comments(comments) || self.tail.has_comments(comments); @@ -137,230 +260,263 @@ impl MemberChain { return Ok(true); } - // Do not allow the group to break if it only contains a single call expression - if self.calls_count <= 1 { - return Ok(false); + let mut call_expressions = self + .members() + .filter_map(|member| match member { + ChainMember::CallExpression { expression, .. } => Some(expression), + _ => None, + }) + .peekable(); + + let mut calls_count = 0u32; + let mut any_has_function_like_argument = false; + let mut any_complex_args = false; + + while let Some(call) = call_expressions.next() { + calls_count += 1; + + if call_expressions.peek().is_some() { + any_has_function_like_argument = + any_has_function_like_argument || has_arrow_or_function_expression_arg(call) + } + + any_complex_args = any_complex_args || !has_simple_arguments(call); + } + + if calls_count > 2 && any_complex_args { + return Ok(true); + } + + if self.last_call_breaks(f)? && any_has_function_like_argument { + return Ok(true); } - // we want to check the simplicity of the call expressions only if we have at least - // two of them - // Check prettier: https://github.com/prettier/prettier/blob/main/src/language-js/print/member-chain.js#L389 - let call_expressions_are_not_simple = - self.calls_count > 2 && self.call_expressions_are_not_simple()?; + if !self.tail.is_empty() && self.head.will_break(f)? { + return Ok(true); + } - // TODO: add here will_break logic + if self.tail.any_except_last_will_break(f)? { + return Ok(true); + } - Ok(call_expressions_are_not_simple) + Ok(false) } /// We retrieve all the call expressions inside the group and we check if /// their arguments are not simple. - fn call_expressions_are_not_simple(&self) -> SyntaxResult { - Ok(self.tail.get_call_expressions().any(|call_expression| { - call_expression.arguments().map_or(false, |arguments| { - !arguments - .args() - .iter() - .filter_map(|argument| argument.ok()) - .all(|argument| SimpleArgument::new(argument).is_simple(0)) - }) - })) + fn last_call_breaks(&self, f: &mut JsFormatter) -> FormatResult { + let last_group = self.last_group(); + + if let Some(ChainMember::CallExpression { .. }) = last_group.members().last() { + last_group.will_break(f) + } else { + Ok(false) + } + } + + fn last_group(&self) -> &MemberChainGroup { + self.tail.last().unwrap_or(&self.head) + } + + /// Returns an iterator over all members in the member chain + fn members(&self) -> impl Iterator + DoubleEndedIterator { + self.head.members().iter().chain(self.tail.members()) + } + + fn has_comments(&self, comments: &JsComments) -> bool { + let mut members = self.members(); + + if let Some(first) = members.next() { + if comments.has_trailing_comments(first.syntax()) { + return true; + } + } + + // Ignore the root member because comments are printed before/after the member chain. + members.next_back(); + + for member in members { + if comments.has_leading_comments(member.syntax()) + || comments.has_trailing_comments(member.syntax()) + { + return true; + } + } + + false } } impl Format for MemberChain { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - // TODO use Alternatives once available - write!(f, [&self.head])?; - - if self.groups_should_break(f.context().comments())? { - write!( - f, - [indent(&format_args!( - hard_line_break(), - format_with(|f| { - f.join_with(hard_line_break()) - .entries(self.tail.iter()) - .finish() - }) - ))] - ) - } else { - write!(f, [self.tail]) + let has_comments = self.has_comments(f.comments()); + + let format_one_line = format_with(|f| { + let mut joiner = f.join(); + + joiner.entry(&self.head); + joiner.entries(self.tail.iter()); + + joiner.finish() + }); + + if self.tail.len() <= 1 && !has_comments { + return if is_long_curried_call(&self.root) { + write!(f, [format_one_line]) + } else { + write!(f, [group(&format_one_line)]) + }; } - } -} -pub(crate) fn get_member_chain( - call_expression: &JsCallExpression, - f: &mut JsFormatter, -) -> SyntaxResult { - let mut chain_members = vec![]; - let parent_is_expression_statement = - call_expression.syntax().parent().map_or(false, |parent| { - JsExpressionStatement::can_cast(parent.kind()) + let has_empty_line = match self.tail.members().next() { + Some(member) => member.needs_empty_line_before(), + None => false, + }; + + let format_tail = format_with(|f| { + if !has_empty_line { + write!(f, [hard_line_break()])?; + } + + f.join_with(hard_line_break()) + .entries(self.tail.iter()) + .finish() }); - let root = flatten_member_chain( - &mut chain_members, - call_expression.clone().into(), - f.context().comments(), - true, - )?; + let format_expanded = format_with(|f| write!(f, [self.head, indent(&group(&format_tail))])); - chain_members.push(root); + let format_content = format_with(|f| { + if self.groups_should_break(f)? { + write!(f, [group(&format_expanded)]) + } else { + if has_empty_line || self.last_group().will_break(f)? { + write!(f, [expand_parent()])?; + } - // Count the number of CallExpression in the chain, - // will be used later to decide on how to format it - let calls_count = chain_members - .iter() - .filter(|item| item.is_loose_call_expression()) - .count(); - - // as explained before, the first group is particular, so we calculate it - let index_to_split_at = compute_first_group_index(&chain_members); - - // we have the index where we want to take the first group - let remaining_groups = chain_members.split_off(index_to_split_at); - let first_group = chain_members; - - let mut head_group = MemberChainGroup::from(first_group); - - // `flattened_items` now contains only the nodes that should have a sequence of - // `[ StaticMemberExpression -> AnyNode + JsCallExpression ]` - let mut rest_of_groups = compute_groups( - remaining_groups.into_iter(), - parent_is_expression_statement, - f, - ); - - // Here we check if the first element of Groups::groups can be moved inside the head. - // If so, then we extract it and concatenate it together with the head. - if let Some(group_to_merge) = - rest_of_groups.should_merge_with_first_group(&head_group, f.comments()) - { - let group_to_merge = group_to_merge - .into_iter() - .flat_map(|group| group.into_members()); - head_group.expand_group(group_to_merge); - } + write!(f, [best_fitting!(format_one_line, format_expanded)]) + } + }); - Ok(MemberChain { - calls_count, - head: head_group, - tail: rest_of_groups, - }) + write!( + f, + [labelled(LabelId::of::(), &format_content)] + ) + } } -/// Retrieves the index where we want to calculate the first group. -/// The first group gathers inside it all those nodes that are not a sequence of something like: -/// `[ StaticMemberExpression -> AnyNode + JsCallExpression ]` -fn compute_first_group_index(flatten_items: &[ChainMember]) -> usize { - flatten_items +/// Splits the members into two groups: +/// * The head group that contains all notes that are not a sequence of: `[ StaticMemberExpression -> AnyNode + JsCallExpression ]` +/// * The remaining members +fn split_members_into_head_and_remaining_groups( + mut members: Vec, +) -> (MemberChainGroup, Vec) { + // This where we apply the first two points explained in the description of the main public function. + // We want to keep iterating over the items until we have call expressions + // - `something()()()()` + // - `something[1][2][4]` + // - `something[1]()[3]()` + // - `something()[2].something.else[0]` + let non_call_or_array_member_access_start = members .iter() .enumerate() - // the first element will always be part of the first group, so we skip it + // The first member is always part of the first group .skip(1) - // we now find the index, all items before this index will belong to the first group - .find_map(|(index, item)| { - let should_skip = match item { - // This where we apply the first two points explained in the description of the main public function. - // We want to keep iterating over the items until we have call expressions or computed expressions: - // - `something()()()()` - // - `something[1][2][4]` - // - `something[1]()[3]()` - // - `something()[2].something.else[0]` - ChainMember::CallExpression { .. } | ChainMember::ComputedMember { .. } => true, - - // SAFETY: The check `flatten_items[index + 1]` will never panic at runtime because - // 1. The array will always have at least two items - // 2. The last element of the array is always a CallExpression - // - // Something like `a()` produces these flatten times: - // ``` - // [ - // Token("a", 0..1), - // CallExpression: [Empty, Empty, Group(List [Token("(", 5..6), Token(")", 2..7)])], - // ] - // ``` - // - // Hence, it will never enter the branch of this `match`. - // - // When we have something like `a.b.c()`, the flatten items produced are: - // - // ``` - // [ - // Token("a", 0..1), - // StaticMember: [Token(".", 1..2), Token("b", 2..3)], - // StaticMember: [Token(".", 3..4), Token("c", 4..5)], - // CallExpression: [Empty, Empty, Group(List [Token("(", 5..6), Token(")", 6..7)])], - // ] - // ``` - // - // The loop will match against `StaticMember: [Token(".", 3..4), Token("c", 4..5)],` - // and the next one is a call expression... the `matches!` fails and the loop is stopped. - // - // The last element of the array is always a `CallExpression`, which allows us to avoid the overflow of the array. - ChainMember::StaticMember { .. } => { - let next_flatten_item = &flatten_items[index + 1]; - matches!( - next_flatten_item, - ChainMember::StaticMember { .. } | ChainMember::ComputedMember { .. } - ) + .find_map(|(index, member)| match member { + ChainMember::CallExpression { .. } + | ChainMember::TsNonNullAssertionExpression { .. } => None, + + ChainMember::ComputedMember { expression } => { + if matches!( + expression.member(), + Ok(JsAnyExpression::JsAnyLiteralExpression( + JsAnyLiteralExpression::JsNumberLiteralExpression(_), + )) + ) { + None + } else { + Some(index) } - _ => false, - }; - - if should_skip { - None - } else { - Some(index) } + + _ => Some(index), }) - // If the above returns None this means either all items were skipped - // or the list was empty. In either case, this means the first group - // covers the entire list of [FlattenItem] - .unwrap_or(flatten_items.len()) + .unwrap_or(members.len()); + + let first_group_end_index = if !members + .first() + .map_or(false, |member| member.is_call_expression()) + { + // Take as many member access chains as possible + let rest = &members[non_call_or_array_member_access_start..]; + let member_end = rest + .iter() + .enumerate() + .find_map(|(index, member)| match member { + ChainMember::StaticMember { .. } | ChainMember::ComputedMember { .. } => { + let next_is_member = matches!( + rest.get(index + 1), + Some(ChainMember::ComputedMember { .. } | ChainMember::StaticMember { .. }) + ); + + (!next_is_member).then_some(index) + } + _ => Some(index), + }) + .unwrap_or(rest.len()); + + non_call_or_array_member_access_start + member_end + } else { + non_call_or_array_member_access_start + }; + + let remaining = members.split_off(first_group_end_index); + (MemberChainGroup::from(members), remaining) } /// computes groups coming after the first group -fn compute_groups( - flatten_items: impl Iterator, - in_expression_statement: bool, - f: &JsFormatter, -) -> MemberChainGroups { +fn compute_remaining_groups(members: Vec, comments: &JsComments) -> TailChainGroups { let mut has_seen_call_expression = false; - let mut groups_builder = - MemberChainGroupsBuilder::new(in_expression_statement, f.options().tab_width()); - for item in flatten_items { - let has_trailing_comments = f.comments().has_trailing_comments(item.syntax()); + let mut groups_builder = MemberChainGroupsBuilder::default(); + + for member in members { + let has_trailing_comments = comments.has_trailing_comments(member.syntax()); + + match member { + // [0] should be appended at the end of the group instead of the + // beginning of the next one + ChainMember::ComputedMember { .. } if is_computed_array_member_access(&member) => { + groups_builder.start_or_continue_group(member); + } - match item { - ChainMember::StaticMember { .. } => { + ChainMember::StaticMember { .. } | ChainMember::ComputedMember { .. } => { // if we have seen a JsCallExpression, we want to close the group. // The resultant group will be something like: [ . , then, () ]; // `.` and `then` belong to the previous StaticMemberExpression, // and `()` belong to the call expression we just encountered - if has_seen_call_expression { groups_builder.close_group(); - groups_builder.start_or_continue_group(item); + groups_builder.start_group(member); has_seen_call_expression = false; } else { - groups_builder.start_or_continue_group(item); + groups_builder.start_or_continue_group(member); } } + ChainMember::CallExpression { .. } => { - let is_loose_call_expression = item.is_loose_call_expression(); - groups_builder.start_or_continue_group(item); - if is_loose_call_expression { - has_seen_call_expression = true; - } + groups_builder.start_or_continue_group(member); + has_seen_call_expression = true; + } + + ChainMember::TsNonNullAssertionExpression { .. } => { + groups_builder.start_or_continue_group(member); } - ChainMember::ComputedMember { .. } => { - groups_builder.start_or_continue_group(item); + + ChainMember::Node(_) if member.is_call_like_expression() => { + groups_builder.start_or_continue_group(member); + has_seen_call_expression = true; } - ChainMember::Node(_) => groups_builder.continue_group(item), + + ChainMember::Node(_) => groups_builder.continue_group(member), } // Close the group immediately if the node had any trailing comments to @@ -368,77 +524,193 @@ fn compute_groups( // were originally commenting if has_trailing_comments { groups_builder.close_group(); + has_seen_call_expression = false; } } - // closing possible loose groups - groups_builder.close_group(); - groups_builder.finish() } -/// This function tries to flatten the AST. It stores nodes and its formatted version -/// inside an vector of [FlattenItem]. The first element of the vector is the last one. -fn flatten_member_chain( - queue: &mut Vec, - node: JsAnyExpression, +fn is_computed_array_member_access(member: &ChainMember) -> bool { + if let ChainMember::ComputedMember { expression } = member { + matches!( + expression.member(), + Ok(JsAnyExpression::JsAnyLiteralExpression( + JsAnyLiteralExpression::JsNumberLiteralExpression(_) + )) + ) + } else { + false + } +} + +fn has_arrow_or_function_expression_arg(call: &JsCallExpression) -> bool { + call.arguments().map_or(false, |arguments| { + arguments.args().iter().any(|argument| { + matches!( + argument, + Ok(JsAnyCallArgument::JsAnyExpression( + JsAnyExpression::JsArrowFunctionExpression(_) + | JsAnyExpression::JsFunctionExpression(_) + )) + ) + }) + }) +} + +fn has_simple_arguments(call: &JsCallExpression) -> bool { + call.arguments().map_or(false, |arguments| { + arguments.args().iter().all(|argument| { + argument.map_or(false, |argument| SimpleArgument::new(argument).is_simple()) + }) + }) +} + +/// In order to detect those cases, we use an heuristic: if the first +/// node is an identifier with the name starting with a capital +/// letter or just a sequence of _$. The rationale is that they are +/// likely to be factories. +fn is_factory(token: &JsSyntaxToken) -> bool { + let text = token.text_trimmed(); + + let mut chars = text.chars(); + + match text.chars().next() { + // Any sequence of '$' or '_' characters + Some('_') | Some('$') => chars.all(|c| matches!(c, '_' | '$')), + Some(c) => c.is_uppercase(), + _ => false, + } +} + +/// Here we check if the length of the groups exceeds the cutoff or there are comments +/// This function is the inverse of the prettier function +/// [Prettier applies]: https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/member-chain.js#L342 +pub fn is_member_call_chain( + expression: JsCallExpression, comments: &JsComments, + tab_width: TabWidth, +) -> SyntaxResult { + let chain = MemberChain::from_call_expression(expression, comments, tab_width)?; + + Ok(chain.tail.is_member_call_chain(comments)) +} + +/// Tests if expression is a long curried call +/// +/// ```javascript +/// `connect(a, b, c)(d)` +/// ``` +fn is_long_curried_call(expression: &JsCallExpression) -> bool { + if let Some(parent_call) = expression.parent::() { + match (expression.arguments(), parent_call.arguments()) { + (Ok(arguments), Ok(parent_arguments)) => { + is_callee(expression.syntax(), parent_call.syntax()) + && arguments.args().len() > parent_arguments.args().len() + && !parent_arguments.args().is_empty() + } + _ => false, + } + } else { + false + } +} + +fn has_short_name(identifier: &JsIdentifierExpression, tab_width: TabWidth) -> bool { + identifier + .name() + .and_then(|name| name.value_token()) + .map_or(false, |name| { + name.text_trimmed().len() <= u8::from(tab_width) as usize + }) +} + +struct ChainMembersIterator<'a> { + next: Option, + comments: &'a JsComments, root: bool, -) -> SyntaxResult { - use JsAnyExpression::*; +} - if comments.is_suppressed(node.syntax()) { - return Ok(ChainMember::Node(node.into_syntax())); +impl<'a> ChainMembersIterator<'a> { + fn new(root: JsAnyExpression, comments: &'a JsComments) -> Self { + Self { + next: Some(root), + comments, + root: true, + } } +} - let member = match node { - JsCallExpression(call_expression) => { - let callee = call_expression.callee()?; - let left = flatten_member_chain(queue, callee, comments, false)?; - queue.push(left); +impl Iterator for ChainMembersIterator<'_> { + type Item = ChainMember; - ChainMember::CallExpression { - expression: call_expression, - root, - } + fn next(&mut self) -> Option { + use JsAnyExpression::*; + + let expression = self.next.take()?; + + if self.comments.is_suppressed(expression.syntax()) { + return Some(ChainMember::Node(expression.into_syntax())); } - JsStaticMemberExpression(static_member) => { - let object = static_member.object()?; - let left = flatten_member_chain(queue, object, comments, false)?; - queue.push(left); + let member = match expression { + JsCallExpression(call_expression) => { + let callee = call_expression.callee().ok(); + + let is_chain = matches!( + callee, + Some( + JsStaticMemberExpression(_) + | JsComputedMemberExpression(_) + | JsCallExpression(_) + ) + ); + + if is_chain { + self.next = callee; + } + + let position = if self.root { + CallExpressionPosition::End + } else if !is_chain { + CallExpressionPosition::Start + } else { + CallExpressionPosition::Middle + }; - ChainMember::StaticMember { - expression: static_member, - root, + ChainMember::CallExpression { + expression: call_expression, + position, + } } - } - JsComputedMemberExpression(computed_expression) => { - let object = computed_expression.object()?; + JsStaticMemberExpression(static_member) => { + self.next = static_member.object().ok(); + ChainMember::StaticMember { + expression: static_member, + } + } - let left = flatten_member_chain(queue, object, comments, false)?; - queue.push(left); + JsComputedMemberExpression(computed_expression) => { + self.next = computed_expression.object().ok(); - ChainMember::ComputedMember { - expression: computed_expression, - root, + ChainMember::ComputedMember { + expression: computed_expression, + } } - } - expression => ChainMember::Node(expression.into_syntax()), - }; - Ok(member) -} + TsNonNullAssertionExpression(expression) => { + self.next = expression.expression().ok(); + ChainMember::TsNonNullAssertionExpression { expression } + } -/// Here we check if the length of the groups exceeds the cutoff or there are comments -/// This function is the inverse of the prettier function -/// [Prettier applies]: https://github.com/prettier/prettier/blob/a043ac0d733c4d53f980aa73807a63fc914f23bd/src/language-js/print/member-chain.js#L342 -pub fn is_member_call_chain( - expression: &JsCallExpression, - f: &mut JsFormatter, -) -> SyntaxResult { - let chain = get_member_chain(expression, f)?; + expression => ChainMember::Node(expression.into_syntax()), + }; + + self.root = false; - Ok(chain.tail.is_member_call_chain(f.context().comments())) + Some(member) + } } + +impl FusedIterator for ChainMembersIterator<'_> {} diff --git a/crates/rome_js_formatter/src/utils/member_chain/simple_argument.rs b/crates/rome_js_formatter/src/utils/member_chain/simple_argument.rs index c0dd0001076..0ace9ad2eca 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/simple_argument.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/simple_argument.rs @@ -38,8 +38,6 @@ use rome_rowan::{AstSeparatedList, SyntaxResult}; pub(crate) enum SimpleArgument { Expression(JsAnyExpression), Name(JsAnyName), - Member(JsAnyObjectMember), - ArrayElement(JsAnyArrayElement), Spread, } @@ -51,7 +49,11 @@ impl SimpleArgument { } } - pub fn is_simple(&self, depth: u8) -> bool { + pub fn is_simple(&self) -> bool { + self.is_simple_impl(0) + } + + fn is_simple_impl(&self, depth: u8) -> bool { if depth >= 2 { return false; } @@ -81,12 +83,12 @@ impl SimpleArgument { let arguments = match any_expression { JsAnyExpression::JsNewExpression(expr) => { let callee = expr.callee()?; - is_simple_callee = SimpleArgument::from(callee).is_simple(depth); + is_simple_callee = SimpleArgument::from(callee).is_simple_impl(depth); expr.arguments() } JsAnyExpression::JsCallExpression(expr) => { let callee = expr.callee()?; - is_simple_callee = SimpleArgument::from(callee).is_simple(depth); + is_simple_callee = SimpleArgument::from(callee).is_simple_impl(depth); expr.arguments().ok() } JsAnyExpression::JsImportCallExpression(expr) => { @@ -99,7 +101,7 @@ impl SimpleArgument { let simple_arguments = if let Some(arguments) = arguments { arguments.args().iter().all(|argument| { argument.map_or(true, |argument| { - SimpleArgument::from(argument).is_simple(depth + 1) + SimpleArgument::from(argument).is_simple_impl(depth + 1) }) }) } else { @@ -125,8 +127,8 @@ impl SimpleArgument { let JsStaticMemberExpressionFields { member, object, .. } = static_expression.as_fields(); - Ok(SimpleArgument::from(member?).is_simple(depth) - && SimpleArgument::from(object?).is_simple(depth)) + Ok(SimpleArgument::from(member?).is_simple_impl(depth) + && SimpleArgument::from(object?).is_simple_impl(depth)) } else { Ok(false) } @@ -137,7 +139,7 @@ impl SimpleArgument { assertion, )) = self { - Ok(SimpleArgument::from(assertion.expression()?).is_simple(depth)) + Ok(SimpleArgument::from(assertion.expression()?).is_simple_impl(depth)) } else { Ok(false) } @@ -151,7 +153,7 @@ impl SimpleArgument { unary_expression.operator()?, JsUnaryOperator::LogicalNot | JsUnaryOperator::Minus ) { - Ok(SimpleArgument::from(unary_expression.argument()?).is_simple(depth)) + Ok(SimpleArgument::from(unary_expression.argument()?).is_simple_impl(depth)) } else { Ok(false) } @@ -168,7 +170,12 @@ impl SimpleArgument { .elements() .iter() .filter_map(|element| element.ok()) - .all(|element| SimpleArgument::from(element).is_simple(depth + 1)) + .all(|element| match element { + JsAnyArrayElement::JsAnyExpression(expression) => { + SimpleArgument::from(expression).is_simple_impl(depth + 1) + } + _ => false, + }) } else { false } @@ -182,20 +189,20 @@ impl SimpleArgument { } } - fn is_simple_literal(&self) -> bool { - match self { - SimpleArgument::Expression(expression) => { - matches!( - expression, - JsAnyExpression::JsAnyLiteralExpression(_) - | JsAnyExpression::JsThisExpression(_) - | JsAnyExpression::JsIdentifierExpression(_) - | JsAnyExpression::JsSuperExpression(_) - ) - } - SimpleArgument::Name(JsAnyName::JsPrivateName(_)) => true, - _ => false, + const fn is_simple_literal(&self) -> bool { + if let SimpleArgument::Name(JsAnyName::JsPrivateName(_)) = self { + return true; } + + matches!( + self, + SimpleArgument::Expression( + JsAnyExpression::JsAnyLiteralExpression(_) + | JsAnyExpression::JsThisExpression(_) + | JsAnyExpression::JsIdentifierExpression(_) + | JsAnyExpression::JsSuperExpression(_), + ) + ) } fn is_simple_object_expression(&self, depth: u8) -> bool { @@ -207,22 +214,24 @@ impl SimpleArgument { .iter() .filter_map(|member| member.ok()) .all(|member| { - let is_shorthand_property = matches!( - member, - JsAnyObjectMember::JsShorthandPropertyObjectMember(_) - ); - let is_computed_property = - if let JsAnyObjectMember::JsPropertyObjectMember(property) = &member { - matches!( + use JsAnyObjectMember::*; + + match member { + JsShorthandPropertyObjectMember(_) => true, + JsPropertyObjectMember(property) => { + let is_computed = matches!( property.name(), Ok(JsAnyObjectMemberName::JsComputedMemberName(_)) - ) - } else { - false - }; - let is_simple = SimpleArgument::from(member).is_simple(depth + 1); + ); - !is_computed_property && (is_shorthand_property || is_simple) + let is_simple = property.value().map_or(false, |value| { + SimpleArgument::from(value).is_simple_impl(depth + 1) + }); + + !is_computed && is_simple + } + _ => false, + } }) } else { false @@ -242,18 +251,6 @@ impl From for SimpleArgument { } } -impl From for SimpleArgument { - fn from(member: JsAnyObjectMember) -> Self { - Self::Member(member) - } -} - -impl From for SimpleArgument { - fn from(element: JsAnyArrayElement) -> Self { - Self::ArrayElement(element) - } -} - impl From for SimpleArgument { fn from(_: JsSpread) -> Self { Self::Spread @@ -284,7 +281,7 @@ pub fn is_simple_template_literal(template: &JsTemplate, depth: u8) -> SyntaxRes } JsAnyTemplateElement::JsTemplateElement(element) => { let expression = element.expression()?; - if !(SimpleArgument::from(expression).is_simple(depth)) { + if !(SimpleArgument::from(expression).is_simple_impl(depth)) { return Ok(false); } } diff --git a/crates/rome_js_formatter/src/utils/mod.rs b/crates/rome_js_formatter/src/utils/mod.rs index 480e32d912d..71f406a33a8 100644 --- a/crates/rome_js_formatter/src/utils/mod.rs +++ b/crates/rome_js_formatter/src/utils/mod.rs @@ -6,7 +6,7 @@ pub mod string_utils; pub(crate) mod format_class; pub mod jsx; -mod member_chain; +pub(crate) mod member_chain; mod object; mod object_like; mod object_pattern_like; @@ -23,7 +23,6 @@ pub(crate) use binary_like_expression::{ needs_binary_like_parentheses, JsAnyBinaryLikeExpression, JsAnyBinaryLikeLeftExpression, }; pub(crate) use conditional::{ConditionalJsxChain, JsAnyConditional}; -pub(crate) use member_chain::get_member_chain; pub(crate) use object_like::JsObjectLike; pub(crate) use object_pattern_like::JsObjectPatternLike; use rome_formatter::{format_args, write, Buffer}; diff --git a/crates/rome_js_formatter/tests/specs/js/module/declarations/variable_declaration.js.snap b/crates/rome_js_formatter/tests/specs/js/module/declarations/variable_declaration.js.snap index f7e95fbf442..f2fe0b7f7cc 100644 --- a/crates/rome_js_formatter/tests/specs/js/module/declarations/variable_declaration.js.snap +++ b/crates/rome_js_formatter/tests/specs/js/module/declarations/variable_declaration.js.snap @@ -426,13 +426,13 @@ let loooooooooooooooooooooooooong2 = /\wwwwwwwwwwwwwwwwww+/gi, ).ewqeqewqweqweqweqweqweqweqw; let loooooooooooooooooooooooooong3 = - objdddddddddectobjdddddddddect( - /\wwwwwwwwwwwwwwwwww+/gi, - )[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + objdddddddddectobjdddddddddect(/\wwwwwwwwwwwwwwwwww+/gi)[ + dsadsadsadsadsadsadsa + ]().ewqoewqoeiowqieopwqie; let loooooooooooooooooooooooooong3 = - objdddddddddectobjdddddddddect( - /\wwwwwwwwwwwwwwwwww+/gi, - )[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + objdddddddddectobjdddddddddect(/\wwwwwwwwwwwwwwwwww+/gi)[ + dsadsadsadsadsadsadsa + ]().ewqoewqoeiowqieopwqie; //JsTemplate argument var loooooooooooooooooooooooooong1 = @@ -444,13 +444,13 @@ let loooooooooooooooooooooooooong2 = `111111111111111111`, ).ewqeqewqweqweqweqweqweqweqw; let loooooooooooooooooooooooooong3 = - objdddddddddectobjddsadsaddddddddect( - `111111111111111111`, - )[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + objdddddddddectobjddsadsaddddddddect(`111111111111111111`)[ + dsadsadsadsadsadsadsa + ]().ewqoewqoeiowqieopwqie; let loooooooooooooooooooooooooong3 = - objdddddddddectobjdddsadaddddddddect( - `111111111111111111`, - )[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + objdddddddddectobjdddsadaddddddddect(`111111111111111111`)[ + dsadsadsadsadsadsadsa + ]().ewqoewqoeiowqieopwqie; // rest JsAnyLiteralExpression var loooooooooooooooooooooooooong1 = @@ -630,18 +630,12 @@ let loooooooooooooooooooooooooong3 = objdddddddddectobjddsadsaddddddddect( // has new line var loooooooooooooooooooooooooong1 = - fnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfn( - `123123 - dsa`, - ); -let loooooooooooooooooooooooooong2 = objdddddddddectobjdddsadsadddddddect( - `123123 -dsa`, -).ewqeqewqweqweqweqweqweqweqw; -let loooooooooooooooooooooooooong3 = objdddddddddectobjddsadsaddddddddect( - `123123 - dsa`, -)[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + fnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfnfn(`123123 + dsa`); +let loooooooooooooooooooooooooong2 = objdddddddddectobjdddsadsadddddddect(`123123 +dsa`).ewqeqewqweqweqweqweqweqweqw; +let loooooooooooooooooooooooooong3 = objdddddddddectobjddsadsaddddddddect(`123123 + dsa`)[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; //JsThisExpression var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = @@ -702,20 +696,14 @@ let looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo // has new line var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - this( - `123123 - dsa`, - ); + this(`123123 + dsa`); let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - this( - `123123 -dsa`, - ).ewqeqewqweqweqweqweqweqweqw; + this(`123123 +dsa`).ewqeqewqweqweqweqweqweqweqw; let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - this( - `123123 - dsa`, - )[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; + this(`123123 + dsa`)[dsadsadsadsadsadsadsa]().ewqoewqoeiowqieopwqie; //lone short argument JsUnaryExpression argument with comment var loooooooooooooooooooooooooong1 = @@ -775,30 +763,32 @@ const a 251: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = 253: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = 255: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = - 367: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 369: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 371: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 375: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 377: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 379: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 381: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = - 385: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 387: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 389: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 391: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = - 395: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 397: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 399: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 402: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = - 407: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 409: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 411: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 416: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 418: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 420: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 424: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 429: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = - 434: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = - 450: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = - 452: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 355: let loooooooooooooooooooooooooong2 = objdddddddddectobjdddsadsadddddddect(`123123 + 357: let loooooooooooooooooooooooooong3 = objdddddddddectobjddsadsaddddddddect(`123123 + 361: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 363: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 365: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 369: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 371: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 373: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 375: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = + 379: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 381: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 383: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 385: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = + 389: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 391: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 393: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 396: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong4 = + 401: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 403: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 405: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 410: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 412: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 414: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 418: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 421: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = + 424: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong3 = + 438: var loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong1 = + 440: let loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong2 = diff --git a/crates/rome_js_formatter/tests/specs/js/module/expression/member-chain/computed.js.snap b/crates/rome_js_formatter/tests/specs/js/module/expression/member-chain/computed.js.snap index c9b5c26d37c..63fd7e5a589 100644 --- a/crates/rome_js_formatter/tests/specs/js/module/expression/member-chain/computed.js.snap +++ b/crates/rome_js_formatter/tests/specs/js/module/expression/member-chain/computed.js.snap @@ -19,7 +19,8 @@ Quote style: Double Quotes Quote properties: As needed ----- nock(/test/) - .matchHeader("Accept", "application/json")[httpMethodNock(method)]("/foo") + .matchHeader("Accept", "application/json") + [httpMethodNock(method)]("/foo") .reply(200, { foo: "bar", }); diff --git a/crates/rome_js_formatter/tests/specs/js/module/expression/static_member_expression.js.snap b/crates/rome_js_formatter/tests/specs/js/module/expression/static_member_expression.js.snap index 40b67388204..9eb4fe55db7 100644 --- a/crates/rome_js_formatter/tests/specs/js/module/expression/static_member_expression.js.snap +++ b/crates/rome_js_formatter/tests/specs/js/module/expression/static_member_expression.js.snap @@ -41,19 +41,27 @@ a?.b.#c; a?.#b.c().d; lorem.ipsum(); -lorem.ipsum().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong(); -lorem()[0]().ipsum().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong(); +lorem + .ipsum() + .looooooooooooooooooooooooooong() + .looooooooooooooooooooooooooong() + .looooooooooooooooooooooooooong(); +lorem()[0]() + .ipsum() + .looooooooooooooooooooooooooong() + .looooooooooooooooooooooooooong() + .looooooooooooooooooooooooooong(); -something()[1]()[3]().items.item.what_else[3]().something().something().then().catcht().else().what_the_hell(); +something()[1]()[3]() + .items.item.what_else[3]() + .something() + .something() + .then() + .catcht() + .else() + .what_the_hell(); some.member.with. // rome-ignore format: Verify that formatting calls into right.format() rather.hard.to.test.because.name.doesnt.format.being.ignored; - -## Lines exceeding width of 80 characters - - 9: lorem.ipsum().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong(); - 10: lorem()[0]().ipsum().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong().looooooooooooooooooooooooooong(); - 12: something()[1]()[3]().items.item.what_else[3]().something().something().then().catcht().else().what_the_hell(); - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/assignment/call-with-template.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/assignment/call-with-template.js.snap deleted file mode 100644 index 78cb353c58b..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/assignment/call-with-template.js.snap +++ /dev/null @@ -1,56 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const result = template(` - if (SOME_VAR === "") {} -`)({ - SOME_VAR: value, -}); - -const output = - template(`function f() %%A%%`)({ - A: t.blockStatement([]), - }); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,6 +1,8 @@ --const result = template(` -+const result = template( -+ ` - if (SOME_VAR === "") {} --`)({ -+`, -+)({ - SOME_VAR: value, - }); - -``` - -# Output - -```js -const result = template( - ` - if (SOME_VAR === "") {} -`, -)({ - SOME_VAR: value, -}); - -const output = template(`function f() %%A%%`)({ - A: t.blockStatement([]), -}); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/assignment/lone-arg.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/assignment/lone-arg.js.snap deleted file mode 100644 index 9598c1f063b..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/assignment/lone-arg.js.snap +++ /dev/null @@ -1,77 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/assignment/lone-arg.js ---- - -# Input - -```js -let vgChannel = pointPositionDefaultRef({ - model, - defaultPos, - channel, -})() - -let vgChannel2 = pointPositionDefaultRef({ model, - defaultPos, - channel, -})() - -const bifornCringerMoshedPerplexSawderGlyphsHa = - someBigFunctionName("foo")("bar"); - -if (true) { - node.id = this.flowParseTypeAnnotatableIdentifier(/*allowPrimitiveOverride*/ true); -} - -const bifornCringerMoshedPerplexSawderGlyphsHb = someBigFunctionName(`foo -`)("bar"); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -15,5 +15,7 @@ - ); - } - --const bifornCringerMoshedPerplexSawderGlyphsHb = someBigFunctionName(`foo --`)("bar"); -+const bifornCringerMoshedPerplexSawderGlyphsHb = someBigFunctionName( -+ `foo -+`, -+)("bar"); -``` - -# Output - -```js -let vgChannel = pointPositionDefaultRef({ - model, - defaultPos, - channel, -})(); - -let vgChannel2 = pointPositionDefaultRef({ model, defaultPos, channel })(); - -const bifornCringerMoshedPerplexSawderGlyphsHa = - someBigFunctionName("foo")("bar"); - -if (true) { - node.id = this.flowParseTypeAnnotatableIdentifier( - /*allowPrimitiveOverride*/ true, - ); -} - -const bifornCringerMoshedPerplexSawderGlyphsHb = someBigFunctionName( - `foo -`, -)("bar"); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/closure-compiler-type-cast.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/closure-compiler-type-cast.js.snap deleted file mode 100644 index 33463b4d748..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/closure-compiler-type-cast.js.snap +++ /dev/null @@ -1,190 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/comments-closure-typecast/closure-compiler-type-cast.js ---- - -# Input - -```js -// test to make sure comments are attached correctly -let inlineComment = /* some comment */ ( - someReallyLongFunctionCall(withLots, ofArguments)); - -let object = { - key: /* some comment */ (someReallyLongFunctionCall(withLots, ofArguments)) -}; - -// preserve parens only for type casts -let assignment = /** @type {string} */ (getValue()); -let value = /** @type {string} */ (this.members[0]).functionCall(); - -functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); - -function returnValue() { - return /** @type {!Array.} */ (['hello', 'you']); -} - -// Only numberOrString is typecast -var newArray = /** @type {array} */ (numberOrString).map(x => x); -var newArray = /** @type {array} */ ((numberOrString)).map(x => x); -var newArray = test(/** @type {array} */ (numberOrString).map(x => x)); -var newArray = test(/** @type {array} */ ((numberOrString)).map(x => x)); - -// The numberOrString.map CallExpression is typecast -var newArray = /** @type {array} */ (numberOrString.map(x => x)); -var newArray = /** @type {array} */ ((numberOrString).map(x => x)); -var newArray = test(/** @type {array} */ (numberOrString.map(x => x))); -var newArray = test(/** @type {array} */ ((numberOrString).map(x => x))); - -test(/** @type {number} */(num) + 1); -test(/** @type {!Array} */(arrOrString).length + 1); -test(/** @type {!Array} */((arrOrString)).length + 1); - -const data = functionCall( - arg1, - arg2, - /** @type {{height: number, width: number}} */ (arg3)); - -const style = /** @type {{ - width: number, - height: number, - marginTop: number, - marginLeft: number, - marginRight: number, - marginBottom: number, -}} */ ({ - width, - height, - ...margins, -}); - -const style2 =/** - * @type {{ - * width: number, - * }} -*/({ - width, -}); - -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -10,7 +10,8 @@ - - // preserve parens only for type casts - let assignment = /** @type {string} */ (getValue()); --let value = /** @type {string} */ (this.members[0]).functionCall(); -+let value = /** @type {string} */ (this.members[0]) -+ .functionCall(); - - functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); - -@@ -19,10 +20,18 @@ - } - - // Only numberOrString is typecast --var newArray = /** @type {array} */ (numberOrString).map((x) => x); --var newArray = /** @type {array} */ (numberOrString).map((x) => x); --var newArray = test(/** @type {array} */ (numberOrString).map((x) => x)); --var newArray = test(/** @type {array} */ (numberOrString).map((x) => x)); -+var newArray = /** @type {array} */ (numberOrString) -+ .map((x) => x); -+var newArray = /** @type {array} */ (numberOrString) -+ .map((x) => x); -+var newArray = test( -+ /** @type {array} */ (numberOrString) -+ .map((x) => x), -+); -+var newArray = test( -+ /** @type {array} */ (numberOrString) -+ .map((x) => x), -+); - - // The numberOrString.map CallExpression is typecast - var newArray = /** @type {array} */ (numberOrString.map((x) => x)); -``` - -# Output - -```js -// test to make sure comments are attached correctly -let inlineComment = /* some comment */ someReallyLongFunctionCall( - withLots, - ofArguments, -); - -let object = { - key: /* some comment */ someReallyLongFunctionCall(withLots, ofArguments), -}; - -// preserve parens only for type casts -let assignment = /** @type {string} */ (getValue()); -let value = /** @type {string} */ (this.members[0]) - .functionCall(); - -functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); - -function returnValue() { - return /** @type {!Array.} */ (["hello", "you"]); -} - -// Only numberOrString is typecast -var newArray = /** @type {array} */ (numberOrString) - .map((x) => x); -var newArray = /** @type {array} */ (numberOrString) - .map((x) => x); -var newArray = test( - /** @type {array} */ (numberOrString) - .map((x) => x), -); -var newArray = test( - /** @type {array} */ (numberOrString) - .map((x) => x), -); - -// The numberOrString.map CallExpression is typecast -var newArray = /** @type {array} */ (numberOrString.map((x) => x)); -var newArray = /** @type {array} */ (numberOrString.map((x) => x)); -var newArray = test(/** @type {array} */ (numberOrString.map((x) => x))); -var newArray = test(/** @type {array} */ (numberOrString.map((x) => x))); - -test(/** @type {number} */ (num) + 1); -test(/** @type {!Array} */ (arrOrString).length + 1); -test(/** @type {!Array} */ (arrOrString).length + 1); - -const data = functionCall( - arg1, - arg2, - /** @type {{height: number, width: number}} */ (arg3), -); - -const style = /** @type {{ - width: number, - height: number, - marginTop: number, - marginLeft: number, - marginRight: number, - marginBottom: number, -}} */ ({ - width, - height, - ...margins, -}); - -const style2 = /** - * @type {{ - * width: number, - * }} - */ ({ - width, -}); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-in-the-middle.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-in-the-middle.js.snap deleted file mode 100644 index a5f7b1a5923..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-in-the-middle.js.snap +++ /dev/null @@ -1,57 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/comments-closure-typecast/comment-in-the-middle.js ---- - -# Input - -```js -var a = -/** - * bla bla bla - * @type {string | - * number - * } -* bla bla bla - */ -//2 - ((window['s'])).toString(); -console.log(a.foo()); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -7,5 +7,6 @@ - * bla bla bla - */ - //2 -- (window["s"]).toString(); -+ (window["s"]) -+ .toString(); - console.log(a.foo()); -``` - -# Output - -```js -var a = - /** - * bla bla bla - * @type {string | - * number - * } - * bla bla bla - */ - //2 - (window["s"]) - .toString(); -console.log(a.foo()); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/functional_compose.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/functional_compose.js.snap index 0d3409439a6..bd79c48bdf7 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/functional_compose.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/functional_compose.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/functional-composition/functional_compose.js --- # Input @@ -57,7 +59,7 @@ this.subscriptions.add( ```diff --- Prettier +++ Rome -@@ -1,51 +1,27 @@ +@@ -1,46 +1,21 @@ -compose( - sortBy((x) => x), - flatten, @@ -111,15 +113,6 @@ this.subscriptions.add( } } - this.subscriptions.add( -- this.componentUpdates -- .pipe(startWith(this.props), distinctUntilChanged(isEqual)) -- .subscribe((props) => {}), -+ this.componentUpdates.pipe( -+ startWith(this.props), -+ distinctUntilChanged(isEqual), -+ ).subscribe((props) => {}), - ); ``` # Output @@ -147,10 +140,9 @@ class A extends B { } this.subscriptions.add( - this.componentUpdates.pipe( - startWith(this.props), - distinctUntilChanged(isEqual), - ).subscribe((props) => {}), + this.componentUpdates + .pipe(startWith(this.props), distinctUntilChanged(isEqual)) + .subscribe((props) => {}), ); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/rxjs_pipe.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/rxjs_pipe.js.snap deleted file mode 100644 index c56eccb195f..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/functional-composition/rxjs_pipe.js.snap +++ /dev/null @@ -1,61 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -import { range } from 'rxjs/observable/range'; -import { map, filter, scan } from 'rxjs/operators'; - -const source$ = range(0, 10); - -source$.pipe( - filter(x => x % 2 === 0), - map(x => x + x), - scan((acc, x) => acc + x, 0) -) -.subscribe(x => console.log(x)) -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -3,10 +3,8 @@ - - const source$ = range(0, 10); - --source$ -- .pipe( -- filter((x) => x % 2 === 0), -- map((x) => x + x), -- scan((acc, x) => acc + x, 0), -- ) -- .subscribe((x) => console.log(x)); -+source$.pipe( -+ filter((x) => x % 2 === 0), -+ map((x) => x + x), -+ scan((acc, x) => acc + x, 0), -+).subscribe((x) => console.log(x)); -``` - -# Output - -```js -import { range } from "rxjs/observable/range"; -import { map, filter, scan } from "rxjs/operators"; - -const source$ = range(0, 10); - -source$.pipe( - filter((x) => x % 2 === 0), - map((x) => x + x), - scan((acc, x) => acc + x, 0), -).subscribe((x) => console.log(x)); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/member/expand.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/member/expand.js.snap deleted file mode 100644 index db88499a1dd..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/member/expand.js.snap +++ /dev/null @@ -1,117 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const veryVeryVeryVeryVeryVeryVeryLong = doc.expandedStates[doc.expandedStates.length - 1]; -const small = doc.expandedStates[doc.expandedStates.length - 1]; - -const promises = [ - promise.resolve().then(console.log).catch(err => { - console.log(err) - return null - }), - redis.fetch(), - other.fetch(), -]; - -const promises2 = [ - promise.resolve().veryLongFunctionCall().veryLongFunctionCall().then(console.log).catch(err => { - console.log(err) - return null - }), - redis.fetch(), - other.fetch(), -]; - -window.FooClient.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token -}).initVerify("foo_container"); - -window.something.FooClient.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token -}).initVerify("foo_container"); - -window.FooClient.something.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token -}).initVerify("foo_container"); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -38,9 +38,7 @@ - authorizationToken: data.token, - }).initVerify("foo_container"); - --window.FooClient.something -- .setVars({ -- locale: getFooLocale({ page }), -- authorizationToken: data.token, -- }) -- .initVerify("foo_container"); -+window.FooClient.something.setVars({ -+ locale: getFooLocale({ page }), -+ authorizationToken: data.token, -+}).initVerify("foo_container"); -``` - -# Output - -```js -const veryVeryVeryVeryVeryVeryVeryLong = - doc.expandedStates[doc.expandedStates.length - 1]; -const small = doc.expandedStates[doc.expandedStates.length - 1]; - -const promises = [ - promise - .resolve() - .then(console.log) - .catch((err) => { - console.log(err); - return null; - }), - redis.fetch(), - other.fetch(), -]; - -const promises2 = [ - promise - .resolve() - .veryLongFunctionCall() - .veryLongFunctionCall() - .then(console.log) - .catch((err) => { - console.log(err); - return null; - }), - redis.fetch(), - other.fetch(), -]; - -window.FooClient.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token, -}).initVerify("foo_container"); - -window.something.FooClient.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token, -}).initVerify("foo_container"); - -window.FooClient.something.setVars({ - locale: getFooLocale({ page }), - authorizationToken: data.token, -}).initVerify("foo_container"); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/13018.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/13018.js.snap deleted file mode 100644 index d3a5c2b1867..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/13018.js.snap +++ /dev/null @@ -1,69 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/method-chain/13018.js ---- - -# Input - -```js -foo(_a).bar().leet(); -foo(-a).bar().leet(); -foo(+a).bar().leet(); -foo(~a).bar().leet(); -foo(++a).bar().leet(); -foo(--a).bar().leet(); -foo(a++).bar().leet(); -foo(a--).bar().leet(); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,20 +1,8 @@ - foo(_a).bar().leet(); - foo(-a).bar().leet(); --foo(+a) -- .bar() -- .leet(); --foo(~a) -- .bar() -- .leet(); --foo(++a) -- .bar() -- .leet(); --foo(--a) -- .bar() -- .leet(); --foo(a++) -- .bar() -- .leet(); --foo(a--) -- .bar() -- .leet(); -+foo(+a).bar().leet(); -+foo(~a).bar().leet(); -+foo(++a).bar().leet(); -+foo(--a).bar().leet(); -+foo(a++).bar().leet(); -+foo(a--).bar().leet(); -``` - -# Output - -```js -foo(_a).bar().leet(); -foo(-a).bar().leet(); -foo(+a).bar().leet(); -foo(~a).bar().leet(); -foo(++a).bar().leet(); -foo(--a).bar().leet(); -foo(a++).bar().leet(); -foo(a--).bar().leet(); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/bracket_0-1.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/bracket_0-1.js.snap deleted file mode 100644 index 0495cddb7fc..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/bracket_0-1.js.snap +++ /dev/null @@ -1,34 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const thingamabobMetaAlias = -path.scope.getProgramParent().path.get("body")[0].node; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,3 +1,2 @@ --const thingamabobMetaAlias = path.scope -- .getProgramParent() -- .path.get("body")[0].node; -+const thingamabobMetaAlias = path.scope.getProgramParent().path.get("body")[0] -+ .node; -``` - -# Output - -```js -const thingamabobMetaAlias = path.scope.getProgramParent().path.get("body")[0] - .node; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/comment.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/comment.js.snap deleted file mode 100644 index 79f10844abd..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/comment.js.snap +++ /dev/null @@ -1,160 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/method-chain/comment.js ---- - -# Input - -```js -function f() { - return observableFromSubscribeFunction() - // Debounce manually rather than using editor.onDidStopChanging so that the debounce time is - // configurable. - .debounceTime(debounceInterval); -} - -_.a(a) - /* very very very very very very very long such that it is longer than 80 columns */ - .a() - -_.a( - a -)/* very very very very very very very long such that it is longer than 80 columns */ -.a(); - -_.a( - a -) /* very very very very very very very long such that it is longer than 80 columns */.a(); - -Something - // $FlowFixMe(>=0.41.0) - .getInstance(this.props.dao) - .getters() - -// Warm-up first -measure() - .then(() => { - SomethingLong(); - }); - -measure() // Warm-up first - .then(() => { - SomethingLong(); - }); - -const configModel = this.baseConfigurationService.getCache().consolidated // global/default values (do NOT modify) - .merge(this.cachedWorkspaceConfig); - -this.doWriteConfiguration(target, value, options) // queue up writes to prevent race conditions - .then(() => null, - error => { - return options.donotNotifyError ? TPromise.wrapError(error) : this.onError(error, target, value); - }); - -ret = __DEV__ ? - // $FlowFixMe: this type differs according to the env -vm.runInContext(source, ctx) -: a - -angular.module('AngularAppModule') - // Hello, I am comment. - .constant('API_URL', 'http://localhost:8080/api'); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -53,7 +53,8 @@ - - ret = __DEV__ - ? // $FlowFixMe: this type differs according to the env -- vm.runInContext(source, ctx) -+ vm -+ .runInContext(source, ctx) - : a; - - angular -``` - -# Output - -```js -function f() { - return ( - observableFromSubscribeFunction() - // Debounce manually rather than using editor.onDidStopChanging so that the debounce time is - // configurable. - .debounceTime(debounceInterval) - ); -} - -_.a(a) - /* very very very very very very very long such that it is longer than 80 columns */ - .a(); - -_.a( - a, -) /* very very very very very very very long such that it is longer than 80 columns */ - .a(); - -_.a( - a, -) /* very very very very very very very long such that it is longer than 80 columns */ - .a(); - -Something - // $FlowFixMe(>=0.41.0) - .getInstance(this.props.dao) - .getters(); - -// Warm-up first -measure().then(() => { - SomethingLong(); -}); - -measure() // Warm-up first - .then(() => { - SomethingLong(); - }); - -const configModel = this.baseConfigurationService - .getCache() - .consolidated // global/default values (do NOT modify) - .merge(this.cachedWorkspaceConfig); - -this.doWriteConfiguration(target, value, options) // queue up writes to prevent race conditions - .then( - () => null, - (error) => { - return options.donotNotifyError - ? TPromise.wrapError(error) - : this.onError(error, target, value); - }, - ); - -ret = __DEV__ - ? // $FlowFixMe: this type differs according to the env - vm - .runInContext(source, ctx) - : a; - -angular - .module("AngularAppModule") - // Hello, I am comment. - .constant("API_URL", "http://localhost:8080/api"); -``` - - -# Lines exceeding max width of 80 characters -``` - 4: // Debounce manually rather than using editor.onDidStopChanging so that the debounce time is - 11: /* very very very very very very very long such that it is longer than 80 columns */ - 16: ) /* very very very very very very very long such that it is longer than 80 columns */ - 21: ) /* very very very very very very very long such that it is longer than 80 columns */ - 44: this.doWriteConfiguration(target, value, options) // queue up writes to prevent race conditions -``` - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed-merge.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed-merge.js.snap deleted file mode 100644 index 7f7700d2c63..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed-merge.js.snap +++ /dev/null @@ -1,65 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -[].forEach(key => { - data[key]('foo') - .then(() => console.log('bar')) - .catch(() => console.log('baz')); -}); - -[].forEach(key => { - data('foo') - [key]('bar') - .then(() => console.log('bar')) - .catch(() => console.log('baz')); -}); - -window.Data[key]("foo") - .then(() => a) - .catch(() => b); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -5,8 +5,7 @@ - }); - - [].forEach((key) => { -- data("foo") -- [key]("bar") -+ data("foo")[key]("bar") - .then(() => console.log("bar")) - .catch(() => console.log("baz")); - }); -``` - -# Output - -```js -[].forEach((key) => { - data[key]("foo") - .then(() => console.log("bar")) - .catch(() => console.log("baz")); -}); - -[].forEach((key) => { - data("foo")[key]("bar") - .then(() => console.log("bar")) - .catch(() => console.log("baz")); -}); - -window.Data[key]("foo") - .then(() => a) - .catch(() => b); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed.js.snap deleted file mode 100644 index f35739c27ef..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/computed.js.snap +++ /dev/null @@ -1,43 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -nock(/test/) - .matchHeader('Accept', 'application/json') - [httpMethodNock(method)]('/foo') - .reply(200, { - foo: 'bar', - }); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,6 +1,5 @@ - nock(/test/) -- .matchHeader("Accept", "application/json") -- [httpMethodNock(method)]("/foo") -+ .matchHeader("Accept", "application/json")[httpMethodNock(method)]("/foo") - .reply(200, { - foo: "bar", - }); -``` - -# Output - -```js -nock(/test/) - .matchHeader("Accept", "application/json")[httpMethodNock(method)]("/foo") - .reply(200, { - foo: "bar", - }); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/conditional.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/conditional.js.snap deleted file mode 100644 index 1da233ef2f1..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/conditional.js.snap +++ /dev/null @@ -1,95 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -(a ? b : c).d(); - -(a ? b : c).d().e(); - -(a ? b : c).d().e().f(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(this.defaultUser)) -.map(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(this.defaultUser)) -.map().filter(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(defaultUser)) -.map(); - -object[valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(defaultUser)] -.map(); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -12,17 +12,13 @@ - (valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(this.defaultUser) --) -- .map() -- .filter(); -+).map().filter(); - - (valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(defaultUser) - ).map(); - --object[ -- valid -- ? helper.responseBody(this.currentUser) -- : helper.responseBody(defaultUser) --].map(); -+object[valid -+ ? helper.responseBody(this.currentUser) -+ : helper.responseBody(defaultUser)].map(); -``` - -# Output - -```js -(a ? b : c).d(); - -(a ? b : c).d().e(); - -(a ? b : c).d().e().f(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(this.defaultUser) -).map(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(this.defaultUser) -).map().filter(); - -(valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(defaultUser) -).map(); - -object[valid - ? helper.responseBody(this.currentUser) - : helper.responseBody(defaultUser)].map(); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/d3.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/d3.js.snap deleted file mode 100644 index d0a59b84fdf..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/d3.js.snap +++ /dev/null @@ -1,87 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -d3.select('body') - .append('circle') - .at({ width: 30, fill: '#f0f' }) - .st({ fontWeight: 600 }) - -const myScale = d3.scaleLinear() - .domain([1950, 1980]) - .range([0, width]) - -not.d3.select('body') - .append('circle') - .at({ width: 30, fill: '#f0f' }) - .st({ fontWeight: 600 }) - -not.d3.scaleLinear() - .domain([1950, 1980]) - .range([0, width]) -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,9 +1,13 @@ --d3.select("body") -+d3 -+ .select("body") - .append("circle") - .at({ width: 30, fill: "#f0f" }) - .st({ fontWeight: 600 }); - --const myScale = d3.scaleLinear().domain([1950, 1980]).range([0, width]); -+const myScale = d3 -+ .scaleLinear() -+ .domain([1950, 1980]) -+ .range([0, width]); - - not.d3 - .select("body") -@@ -11,4 +15,7 @@ - .at({ width: 30, fill: "#f0f" }) - .st({ fontWeight: 600 }); - --not.d3.scaleLinear().domain([1950, 1980]).range([0, width]); -+not.d3 -+ .scaleLinear() -+ .domain([1950, 1980]) -+ .range([0, width]); -``` - -# Output - -```js -d3 - .select("body") - .append("circle") - .at({ width: 30, fill: "#f0f" }) - .st({ fontWeight: 600 }); - -const myScale = d3 - .scaleLinear() - .domain([1950, 1980]) - .range([0, width]); - -not.d3 - .select("body") - .append("circle") - .at({ width: 30, fill: "#f0f" }) - .st({ fontWeight: 600 }); - -not.d3 - .scaleLinear() - .domain([1950, 1980]) - .range([0, width]); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/first_long.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/first_long.js.snap index 770e9ad34c2..b271e047198 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/first_long.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/first_long.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/method-chain/first_long.js --- # Input @@ -79,34 +81,6 @@ function f() { ); } -@@ -19,15 +20,16 @@ - return this._getWorker(workerOptions)({ - filePath, - hasteImplModulePath: this._options.hasteImplModulePath, -- }).then((metadata) => { -- // `1` for truthy values instead of `true` to save cache space. -- fileMetadata[H.VISITED] = 1; -- const metadataId = metadata.id; -- const metadataModule = metadata.module; -- if (metadataId && metadataModule) { -- fileMetadata[H.ID] = metadataId; -- setModule(metadataId, metadataModule); -- } -- fileMetadata[H.DEPENDENCIES] = metadata.dependencies || []; -- }); -+ }) -+ .then((metadata) => { -+ // `1` for truthy values instead of `true` to save cache space. -+ fileMetadata[H.VISITED] = 1; -+ const metadataId = metadata.id; -+ const metadataModule = metadata.module; -+ if (metadataId && metadataModule) { -+ fileMetadata[H.ID] = metadataId; -+ setModule(metadataId, metadataModule); -+ } -+ fileMetadata[H.DEPENDENCIES] = metadata.dependencies || []; -+ }); - } ``` # Output @@ -134,18 +108,17 @@ function f() { return this._getWorker(workerOptions)({ filePath, hasteImplModulePath: this._options.hasteImplModulePath, - }) - .then((metadata) => { - // `1` for truthy values instead of `true` to save cache space. - fileMetadata[H.VISITED] = 1; - const metadataId = metadata.id; - const metadataModule = metadata.module; - if (metadataId && metadataModule) { - fileMetadata[H.ID] = metadataId; - setModule(metadataId, metadataModule); - } - fileMetadata[H.DEPENDENCIES] = metadata.dependencies || []; - }); + }).then((metadata) => { + // `1` for truthy values instead of `true` to save cache space. + fileMetadata[H.VISITED] = 1; + const metadataId = metadata.id; + const metadataModule = metadata.module; + if (metadataId && metadataModule) { + fileMetadata[H.ID] = metadataId; + setModule(metadataId, metadataModule); + } + fileMetadata[H.DEPENDENCIES] = metadata.dependencies || []; + }); } ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/fluent-configuration.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/fluent-configuration.js.snap deleted file mode 100644 index 7eaadce662d..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/fluent-configuration.js.snap +++ /dev/null @@ -1,63 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -domain - .concept('Page') - .val('title', 'string') - .vals('widgets', 'Widget') -domain - .concept('Widget') - .val('title', 'string') - .val('color', 'Color') - .val('foo', 'Foo') - .val('bar', 'Bar') -domain - .concept('Widget') - .val('title', 'string') - .val('color', 'Color') -domain - .concept(CONCEPT_NAME) - .val('title') - .vals() -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,9 +1,7 @@ - domain.concept("Page").val("title", "string").vals("widgets", "Widget"); --domain -- .concept("Widget") -- .val("title", "string") -- .val("color", "Color") -- .val("foo", "Foo") -- .val("bar", "Bar"); -+domain.concept("Widget").val("title", "string").val("color", "Color").val( -+ "foo", -+ "Foo", -+).val("bar", "Bar"); - domain.concept("Widget").val("title", "string").val("color", "Color"); - domain.concept(CONCEPT_NAME).val("title").vals(); -``` - -# Output - -```js -domain.concept("Page").val("title", "string").vals("widgets", "Widget"); -domain.concept("Widget").val("title", "string").val("color", "Color").val( - "foo", - "Foo", -).val("bar", "Bar"); -domain.concept("Widget").val("title", "string").val("color", "Color"); -domain.concept(CONCEPT_NAME).val("title").vals(); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-3594.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-3594.js.snap deleted file mode 100644 index f4e05bbeb23..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-3594.js.snap +++ /dev/null @@ -1,53 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const fetched = fetch("/foo"); -fetched - .then(response => response.json()) - .then(json => processThings(json.data.things)); - -let column = new Column(null, conn) - .table(data.table) - .json(data.column); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,6 +1,8 @@ - const fetched = fetch("/foo"); --fetched -- .then((response) => response.json()) -- .then((json) => processThings(json.data.things)); -+fetched.then((response) => response.json()).then( -+ (json) => processThings(json.data.things), -+); - --let column = new Column(null, conn).table(data.table).json(data.column); -+let column = new Column(null, conn) -+ .table(data.table) -+ .json(data.column); -``` - -# Output - -```js -const fetched = fetch("/foo"); -fetched.then((response) => response.json()).then( - (json) => processThings(json.data.things), -); - -let column = new Column(null, conn) - .table(data.table) - .json(data.column); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-4125.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-4125.js.snap index f568ae0a27d..642f3bb8987 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-4125.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/issue-4125.js.snap @@ -164,63 +164,7 @@ var l = base ```diff --- Prettier +++ Rome -@@ -44,7 +44,8 @@ - - // examples from https://github.com/prettier/prettier/issues/1565 - --d3.select("body") -+d3 -+ .select("body") - .selectAll("p") - .data([1, 2, 3]) - .enter() -@@ -67,7 +68,10 @@ - - db.branch( - db.table("users").filter({ email }).count(), -- db.table("users").filter({ email: "a@b.com" }).count(), -+ db -+ .table("users") -+ .filter({ email: "a@b.com" }) -+ .count(), - db.table("users").insert({ email }), - db.table("users").filter({ email }), - ); -@@ -90,39 +94,39 @@ - authorizationToken: data.token, - }).initVerify("foo_container"); - -- fejax -- .ajax({ -- url: "/verification/", -- dataType: "json", -- }) -- .then( -- (data) => { -- this.setState({ isLoading: false }); -- this.initWidget(data); -- }, -- (data) => { -- this.logImpression("foo_fetch_error", data); -- Flash.error(I18n.t("offline_identity.foo_issue")); -- }, -- ); -+ fejax.ajax({ -+ url: "/verification/", -+ dataType: "json", -+ }).then( -+ (data) => { -+ this.setState({ isLoading: false }); -+ this.initWidget(data); -+ }, -+ (data) => { -+ this.logImpression("foo_fetch_error", data); -+ Flash.error(I18n.t("offline_identity.foo_issue")); -+ }, -+ ); - } - - action$ +@@ -111,18 +111,20 @@ .ofType(ActionTypes.SEARCHED_USERS) .map((action) => action.payload.query) .filter((q) => !!q) @@ -252,7 +196,7 @@ var l = base ); window.FooClient.setVars({ -@@ -138,21 +142,32 @@ +@@ -138,10 +140,18 @@ const a1 = x.a(true).b(null).c(123); const a2 = x.d("").e(``).f(g); const a3 = x.d("").e(`${123}`).f(g); @@ -273,24 +217,7 @@ var l = base } } - // should break when call expressions get complex --x.a() -+x -+ .a() - .b([c, [d, [e]]]) - .f(); --x.a() -+x -+ .a() - .b(c(d(e()))) - .f(); --x.a() -+x -+ .a() - .b(`${c(d())}`) - .f(); - -@@ -161,7 +176,4 @@ +@@ -161,7 +171,4 @@ .b() .c(a(a(b(c(d().p).p).p).p)); @@ -350,8 +277,7 @@ function palindrome(a, b) { // examples from https://github.com/prettier/prettier/issues/1565 -d3 - .select("body") +d3.select("body") .selectAll("p") .data([1, 2, 3]) .enter() @@ -374,10 +300,7 @@ something() db.branch( db.table("users").filter({ email }).count(), - db - .table("users") - .filter({ email: "a@b.com" }) - .count(), + db.table("users").filter({ email: "a@b.com" }).count(), db.table("users").insert({ email }), db.table("users").filter({ email }), ); @@ -400,19 +323,21 @@ function HelloWorld() { authorizationToken: data.token, }).initVerify("foo_container"); - fejax.ajax({ - url: "/verification/", - dataType: "json", - }).then( - (data) => { - this.setState({ isLoading: false }); - this.initWidget(data); - }, - (data) => { - this.logImpression("foo_fetch_error", data); - Flash.error(I18n.t("offline_identity.foo_issue")); - }, - ); + fejax + .ajax({ + url: "/verification/", + dataType: "json", + }) + .then( + (data) => { + this.setState({ isLoading: false }); + this.initWidget(data); + }, + (data) => { + this.logImpression("foo_fetch_error", data); + Flash.error(I18n.t("offline_identity.foo_issue")); + }, + ); } action$ @@ -464,16 +389,13 @@ class X { } // should break when call expressions get complex -x - .a() +x.a() .b([c, [d, [e]]]) .f(); -x - .a() +x.a() .b(c(d(e()))) .f(); -x - .a() +x.a() .b(`${c(d())}`) .f(); diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/logical.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/logical.js.snap index a487a31ffa5..7a97dc8e1d5 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/logical.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/logical.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/method-chain/logical.js --- # Input @@ -30,7 +32,7 @@ const someLongVariableName = (idx( ```diff --- Prettier +++ Rome -@@ -2,13 +2,13 @@ +@@ -2,8 +2,8 @@ idx(this.props, (props) => props.someLongPropertyName) || [] ).map((edge) => edge.node); @@ -40,25 +42,7 @@ const someLongVariableName = (idx( + (tickets) => TicketRecord.createFromSomeLongString(), ); --(veryLongVeryLongVeryLong || e) -- .map((tickets) => TicketRecord.createFromSomeLongString()) -- .filter((obj) => !!obj); -+(veryLongVeryLongVeryLong || e).map( -+ (tickets) => TicketRecord.createFromSomeLongString(), -+).filter((obj) => !!obj); - - ( - veryLongVeryLongVeryLong || -@@ -20,6 +20,6 @@ - veryLongVeryLongVeryLong || - anotherVeryLongVeryLongVeryLong || - veryVeryVeryLongError --) -- .map((tickets) => TicketRecord.createFromSomeLongString()) -- .filter((obj) => !!obj); -+).map((tickets) => TicketRecord.createFromSomeLongString()).filter( -+ (obj) => !!obj, -+); + (veryLongVeryLongVeryLong || e) ``` # Output @@ -72,9 +56,9 @@ const someLongVariableName = ( (tickets) => TicketRecord.createFromSomeLongString(), ); -(veryLongVeryLongVeryLong || e).map( - (tickets) => TicketRecord.createFromSomeLongString(), -).filter((obj) => !!obj); +(veryLongVeryLongVeryLong || e) + .map((tickets) => TicketRecord.createFromSomeLongString()) + .filter((obj) => !!obj); ( veryLongVeryLongVeryLong || @@ -86,9 +70,9 @@ const someLongVariableName = ( veryLongVeryLongVeryLong || anotherVeryLongVeryLongVeryLong || veryVeryVeryLongError -).map((tickets) => TicketRecord.createFromSomeLongString()).filter( - (obj) => !!obj, -); +) + .map((tickets) => TicketRecord.createFromSomeLongString()) + .filter((obj) => !!obj); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/multiple-members.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/multiple-members.js.snap deleted file mode 100644 index 51a1dc784ae..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/multiple-members.js.snap +++ /dev/null @@ -1,133 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -if (testConfig.ENABLE_ONLINE_TESTS === "true") { - describe("POST /users/me/pet", function() { - it("saves pet", function() { - function assert(pet) { - expect(pet).to.have.property("OwnerAddress").that.deep.equals({ - AddressLine1: "Alexanderstrasse", - AddressLine2: "", - PostalCode: "10999", - Region: "Berlin", - City: "Berlin", - Country: "DE" - }); - } - }); - }); -} - -wrapper.find('SomewhatLongNodeName').prop('longPropFunctionName')().then(function() { - doSomething(); -}); - -wrapper.find('SomewhatLongNodeName').prop('longPropFunctionName')('argument').then(function() { - doSomething(); -}); - -wrapper.find('SomewhatLongNodeName').prop('longPropFunctionName', 'second argument that pushes this group past 80 characters')('argument').then(function() { - doSomething(); -}); - -wrapper.find('SomewhatLongNodeName').prop('longPropFunctionName')('argument', 'second argument that pushes this group past 80 characters').then(function() { - doSomething(); -}); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -2,14 +2,16 @@ - describe("POST /users/me/pet", function () { - it("saves pet", function () { - function assert(pet) { -- expect(pet).to.have.property("OwnerAddress").that.deep.equals({ -- AddressLine1: "Alexanderstrasse", -- AddressLine2: "", -- PostalCode: "10999", -- Region: "Berlin", -- City: "Berlin", -- Country: "DE", -- }); -+ expect(pet).to.have -+ .property("OwnerAddress") -+ .that.deep.equals({ -+ AddressLine1: "Alexanderstrasse", -+ AddressLine2: "", -+ PostalCode: "10999", -+ Region: "Berlin", -+ City: "Berlin", -+ Country: "DE", -+ }); - } - }); - }); -``` - -# Output - -```js -if (testConfig.ENABLE_ONLINE_TESTS === "true") { - describe("POST /users/me/pet", function () { - it("saves pet", function () { - function assert(pet) { - expect(pet).to.have - .property("OwnerAddress") - .that.deep.equals({ - AddressLine1: "Alexanderstrasse", - AddressLine2: "", - PostalCode: "10999", - Region: "Berlin", - City: "Berlin", - Country: "DE", - }); - } - }); - }); -} - -wrapper - .find("SomewhatLongNodeName") - .prop("longPropFunctionName")() - .then(function () { - doSomething(); - }); - -wrapper - .find("SomewhatLongNodeName") - .prop("longPropFunctionName")("argument") - .then(function () { - doSomething(); - }); - -wrapper - .find("SomewhatLongNodeName") - .prop( - "longPropFunctionName", - "second argument that pushes this group past 80 characters", - )("argument") - .then(function () { - doSomething(); - }); - -wrapper - .find("SomewhatLongNodeName") - .prop("longPropFunctionName")( - "argument", - "second argument that pushes this group past 80 characters", - ) - .then(function () { - doSomething(); - }); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/square_0.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/square_0.js.snap deleted file mode 100644 index 6bf85f30612..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/square_0.js.snap +++ /dev/null @@ -1,52 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const version = someLongString - .split('jest version =') - .pop() - .split(EOL)[0] - .trim(); - -const component = find('.org-lclp-edit-copy-url-banner__link')[0] - .getAttribute('href') - .indexOf(this.landingPageLink); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,8 +1,6 @@ --const version = someLongString -- .split("jest version =") -- .pop() -- .split(EOL)[0] -- .trim(); -+const version = someLongString.split("jest version =").pop().split( -+ EOL, -+)[0].trim(); - - const component = find(".org-lclp-edit-copy-url-banner__link")[0] - .getAttribute("href") -``` - -# Output - -```js -const version = someLongString.split("jest version =").pop().split( - EOL, -)[0].trim(); - -const component = find(".org-lclp-edit-copy-url-banner__link")[0] - .getAttribute("href") - .indexOf(this.landingPageLink); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/test.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/test.js.snap index f62128a3728..4b8c0f72f45 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/test.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/test.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/method-chain/test.js --- # Input @@ -19,12 +21,9 @@ method().then(x => x) ```diff --- Prettier +++ Rome -@@ -1,7 +1,5 @@ - method() -- .then((x) => x) -- ["abc"]((x) => x) -- [abc]((x) => x); -+ .then((x) => x)["abc"]((x) => x)[abc]((x) => x); +@@ -3,5 +3,5 @@ + ["abc"]((x) => x) + [abc]((x) => x); -({}.a().b()); -({}.a().b()); @@ -36,7 +35,9 @@ method().then(x => x) ```js method() - .then((x) => x)["abc"]((x) => x)[abc]((x) => x); + .then((x) => x) + ["abc"]((x) => x) + [abc]((x) => x); ({}).a().b(); ({}).a().b(); diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/this.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/this.js.snap deleted file mode 100644 index 36200454a39..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/method-chain/this.js.snap +++ /dev/null @@ -1,37 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const sel = this.connections - .concat(this.activities.concat(this.operators)) - .filter(x => x.selected); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,3 +1,3 @@ --const sel = this.connections -- .concat(this.activities.concat(this.operators)) -- .filter((x) => x.selected); -+const sel = this.connections.concat( -+ this.activities.concat(this.operators), -+).filter((x) => x.selected); -``` - -# Output - -```js -const sel = this.connections.concat( - this.activities.concat(this.operators), -).filter((x) => x.selected); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/multiparser-css/issue-11797.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/multiparser-css/issue-11797.js.snap index 3133be540b8..badb770b8b9 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/multiparser-css/issue-11797.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/multiparser-css/issue-11797.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/multiparser-css/issue-11797.js --- # Input diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/optional-chaining/comments.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/optional-chaining/comments.js.snap deleted file mode 100644 index cee76a51803..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/optional-chaining/comments.js.snap +++ /dev/null @@ -1,148 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: js/optional-chaining/comments.js ---- - -# Input - -```js -function foo() { - return a - .b() - .c() - // Comment - ?.d() -} - -fooBar - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) - - // App configuration. - .doOneMoreThing(config) - - ?.run(() => console.log("Bar")); - -bigDeal - - .doSomething("Hello World") - - // Hello world - ?.doAnotherThing("Foo", { foo: bar }) - - // App configuration. - .doOneMoreThing(config) - - ?.run(() => console.log("Bar")); - -foo.bar.baz - - ?.doSomething("Hello World") - - // Hello world - .foo.bar.doAnotherThing("Foo", { foo: bar }) - - .doOneMoreThing(config) - ?.bar.run(() => console.log("Bar")); - -(somethingGood ? thisIsIt : maybeNot) - -// Hello world -.doSomething("Hello World") - - ?.doAnotherThing("Foo", { foo: bar }) // Run this - .run(() => console.log("Bar")); // Do this -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -11,38 +11,27 @@ - fooBar - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) -- - // App configuration. - .doOneMoreThing(config) -- - ?.run(() => console.log("Bar")); - - bigDeal -- - .doSomething("Hello World") -- - // Hello world - ?.doAnotherThing("Foo", { foo: bar }) -- - // App configuration. - .doOneMoreThing(config) -- - ?.run(() => console.log("Bar")); - - foo.bar.baz -- - ?.doSomething("Hello World") -- - // Hello world - .foo.bar.doAnotherThing("Foo", { foo: bar }) -- - .doOneMoreThing(config) - ?.bar.run(() => console.log("Bar")); - - (somethingGood ? thisIsIt : maybeNot) -- - // Hello world - .doSomething("Hello World") -- - ?.doAnotherThing("Foo", { foo: bar }) // Run this - .run(() => console.log("Bar")); // Do this -``` - -# Output - -```js -function foo() { - return ( - a - .b() - .c() - // Comment - ?.d() - ); -} - -fooBar - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) - // App configuration. - .doOneMoreThing(config) - ?.run(() => console.log("Bar")); - -bigDeal - .doSomething("Hello World") - // Hello world - ?.doAnotherThing("Foo", { foo: bar }) - // App configuration. - .doOneMoreThing(config) - ?.run(() => console.log("Bar")); - -foo.bar.baz - ?.doSomething("Hello World") - // Hello world - .foo.bar.doAnotherThing("Foo", { foo: bar }) - .doOneMoreThing(config) - ?.bar.run(() => console.log("Bar")); - -(somethingGood ? thisIsIt : maybeNot) - // Hello world - .doSomething("Hello World") - ?.doAnotherThing("Foo", { foo: bar }) // Run this - .run(() => console.log("Bar")); // Do this -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/preserve-line/member-chain.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/preserve-line/member-chain.js.snap deleted file mode 100644 index 2f0a975237c..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/preserve-line/member-chain.js.snap +++ /dev/null @@ -1,191 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -fooBar.doSomething('Hello World').doAnotherThing('Foo', { foo: bar }) - - // App configuration. - .doOneMoreThing(config) - - .run(() => console.log('Bar')); - -bigDeal - - .doSomething('Hello World') - - // Hello world - .doAnotherThing('Foo', { foo: bar }) - - // App configuration. - .doOneMoreThing(config) - - .run(() => console.log('Bar')); - - -foo.bar.baz - - .doSomething('Hello World') - - // Hello world - .foo.bar.doAnotherThing('Foo', { foo: bar }) - - .doOneMoreThing(config) - .bar.run(() => console.log('Bar')); - -( - somethingGood ? thisIsIt : maybeNot -) - - // Hello world - .doSomething('Hello World') - - .doAnotherThing('Foo', { foo: bar }) // Run this - .run(() => console.log('Bar')); // Do this - -helloWorld - - .text() - - .then(t => t); - -(veryLongVeryLongVeryLong || - anotherVeryLongVeryLongVeryLong || - veryVeryVeryLongError -) - - .map(tickets => TicketRecord.createFromSomeLongString()) - - .filter(obj => !!obj); - -const sel = this.connections - - .concat(this.activities.concat(this.operators)) - .filter(x => x.selected); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,59 +1,41 @@ - fooBar - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) -- - // App configuration. - .doOneMoreThing(config) -- - .run(() => console.log("Bar")); - - bigDeal -- - .doSomething("Hello World") -- - // Hello world - .doAnotherThing("Foo", { foo: bar }) -- - // App configuration. - .doOneMoreThing(config) -- - .run(() => console.log("Bar")); - - foo.bar.baz -- - .doSomething("Hello World") -- - // Hello world - .foo.bar.doAnotherThing("Foo", { foo: bar }) -- - .doOneMoreThing(config) - .bar.run(() => console.log("Bar")); - - (somethingGood ? thisIsIt : maybeNot) -- - // Hello world - .doSomething("Hello World") -- - .doAnotherThing("Foo", { foo: bar }) // Run this - .run(() => console.log("Bar")); // Do this - --helloWorld -- -- .text() -- -- .then((t) => t); -+helloWorld.text().then((t) => t); - - ( - veryLongVeryLongVeryLong || - anotherVeryLongVeryLongVeryLong || - veryVeryVeryLongError --) -+).map((tickets) => TicketRecord.createFromSomeLongString()).filter( -+ (obj) => !!obj, -+); - -- .map((tickets) => TicketRecord.createFromSomeLongString()) -- -- .filter((obj) => !!obj); -- --const sel = this.connections -- -- .concat(this.activities.concat(this.operators)) -- .filter((x) => x.selected); -+const sel = this.connections.concat( -+ this.activities.concat(this.operators), -+).filter((x) => x.selected); -``` - -# Output - -```js -fooBar - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) - // App configuration. - .doOneMoreThing(config) - .run(() => console.log("Bar")); - -bigDeal - .doSomething("Hello World") - // Hello world - .doAnotherThing("Foo", { foo: bar }) - // App configuration. - .doOneMoreThing(config) - .run(() => console.log("Bar")); - -foo.bar.baz - .doSomething("Hello World") - // Hello world - .foo.bar.doAnotherThing("Foo", { foo: bar }) - .doOneMoreThing(config) - .bar.run(() => console.log("Bar")); - -(somethingGood ? thisIsIt : maybeNot) - // Hello world - .doSomething("Hello World") - .doAnotherThing("Foo", { foo: bar }) // Run this - .run(() => console.log("Bar")); // Do this - -helloWorld.text().then((t) => t); - -( - veryLongVeryLongVeryLong || - anotherVeryLongVeryLongVeryLong || - veryVeryVeryLongError -).map((tickets) => TicketRecord.createFromSomeLongString()).filter( - (obj) => !!obj, -); - -const sel = this.connections.concat( - this.activities.concat(this.operators), -).filter((x) => x.selected); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/require/require.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/require/require.js.snap deleted file mode 100644 index 8e656bc6f83..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/require/require.js.snap +++ /dev/null @@ -1,75 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const { one, two, three, four, five, six, seven, eight, nine, ten } = require('./my-utils'); -const { one1, two1, three1, four1, five1, six1, seven1, eight1, nine1, ten1, eleven1 } = require('./my-utils'); - -const MyReallyExtrememlyLongModuleName = require('MyReallyExtrememlyLongModuleName'); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,15 +1,6 @@ --const { -- one, -- two, -- three, -- four, -- five, -- six, -- seven, -- eight, -- nine, -- ten, --} = require("./my-utils"); -+const { one, two, three, four, five, six, seven, eight, nine, ten } = require( -+ "./my-utils", -+); - const { - one1, - two1, -@@ -24,4 +15,6 @@ - eleven1, - } = require("./my-utils"); - --const MyReallyExtrememlyLongModuleName = require("MyReallyExtrememlyLongModuleName"); -+const MyReallyExtrememlyLongModuleName = require( -+ "MyReallyExtrememlyLongModuleName", -+); -``` - -# Output - -```js -const { one, two, three, four, five, six, seven, eight, nine, ten } = require( - "./my-utils", -); -const { - one1, - two1, - three1, - four1, - five1, - six1, - seven1, - eight1, - nine1, - ten1, - eleven1, -} = require("./my-utils"); - -const MyReallyExtrememlyLongModuleName = require( - "MyReallyExtrememlyLongModuleName", -); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/template-literals/styled-jsx-with-expressions.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/template-literals/styled-jsx-with-expressions.js.snap index cd5d8e7ba17..504be946b00 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/template-literals/styled-jsx-with-expressions.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/template-literals/styled-jsx-with-expressions.js.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: js/template-literals/styled-jsx-with-expressions.js --- # Input diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/template/call.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/template/call.js.snap deleted file mode 100644 index fa5cde20fe2..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/template/call.js.snap +++ /dev/null @@ -1,73 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -insertRule(`*, *:before, *:after { - box-sizing: inherit; -}`); - -insertRule`*, *:before, *:after { - box-sizing: inherit; -}`; - -new Error(formatErrorMessage` - This a really bad error. - Which has more than one line. -`); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,12 +1,16 @@ --insertRule(`*, *:before, *:after { -+insertRule( -+ `*, *:before, *:after { - box-sizing: inherit; --}`); -+}`, -+); - - insertRule`*, *:before, *:after { - box-sizing: inherit; - }`; - --new Error(formatErrorMessage` -+new Error( -+ formatErrorMessage` - This a really bad error. - Which has more than one line. --`); -+`, -+); -``` - -# Output - -```js -insertRule( - `*, *:before, *:after { - box-sizing: inherit; -}`, -); - -insertRule`*, *:before, *:after { - box-sizing: inherit; -}`; - -new Error( - formatErrorMessage` - This a really bad error. - Which has more than one line. -`, -); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/template/inline.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/template/inline.js.snap deleted file mode 100644 index 5199d4b49c9..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/template/inline.js.snap +++ /dev/null @@ -1,99 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -this._pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest}`); -this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeTest}` -); -this._pipe - .write( - `\n\n Pattern matches ${total} ${pluralizeTest}` - ); - -this._pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest} but that's long`); - -this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeTest} but that's long` -); - -this._pipe.write(` - \n\n Pattern matches ${total} ${pluralizeTest} but that's long -`); - - -() => ` - a -`; - -() => - ` - a - `; - - -// https://github.com/prettier/prettier/issues/5529 -editTitle += `${iconHTML({ class: "reply-to-glyph" })}`; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -10,9 +10,11 @@ - `\n\n Pattern matches ${total} ${pluralizeTest} but that's long`, - ); - --this._pipe.write(` -+this._pipe.write( -+ ` - \n\n Pattern matches ${total} ${pluralizeTest} but that's long --`); -+`, -+); - - () => ` - a -``` - -# Output - -```js -this._pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest}`); -this._pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest}`); -this._pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest}`); - -this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeTest} but that's long`, -); - -this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeTest} but that's long`, -); - -this._pipe.write( - ` - \n\n Pattern matches ${total} ${pluralizeTest} but that's long -`, -); - -() => ` - a -`; - -() => - ` - a - `; - -// https://github.com/prettier/prettier/issues/5529 -editTitle += `${iconHTML({ class: "reply-to-glyph" })}`; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/ternaries/indent-after-paren.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/ternaries/indent-after-paren.js.snap deleted file mode 100644 index 5117448408d..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/ternaries/indent-after-paren.js.snap +++ /dev/null @@ -1,675 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -foo7 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo]; - -foo8 = (condition ? firstValue : secondValue)[SomeType]; - -const foo9 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo]; - -function foo10() { - return (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo]; -} - -function foo11() { - throw (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo]; -} - -function foo12() { - void (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo]; -} - -foo13 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -).Fooooooooooo.Fooooooooooo; - -foo14 = (condition ? firstValue : secondValue)[SomeType]; - -const foo15 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -).Fooooooooooo.Fooooooooooo; - -function foo16() { - return (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -function foo17() { - throw (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -function foo18() { - void (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -foo19 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); - -foo20 = (condition ? firstValue : secondValue)[SomeType]; - -const foo21 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); - -function foo22() { - return (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); -} - -function foo23() { - throw (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); -} - -function foo24() { - void (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); -} - -foo25 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); - -foo26 = (condition ? firstValue : secondValue)[SomeType]; - -const foo27 = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); - -function foo28() { - return (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); -} - -function foo29() { - throw (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); -} - -function foo30() { - void (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); -} - -function* foo31() { - yield (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo); - yield (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)(Fooooooooooo.Fooooooooooo); - yield (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz).Fooooooooooo.Fooooooooooo; - yield (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)[Fooooooooooo.Fooooooooooo]; -} - -const foo32 = new (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -function foo33() { - return new (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo34() { - throw new (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo35() { - void new (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -foo36 = new (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)[AnnularCooeedSplicesWalksWayWay]); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)[AnnularCooeedSplicesWalksWayWay]); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol).Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol).Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo)); - -bifornCringerMoshedPerplexSawder = ( - glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).annularCooeedSplicesWalksWayWay - .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay) - .annularCooeedSplicesWalksWayWay(); - -foo = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)?.()?.()?.(); - -foo = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)()()(); - -foo = - foo.bar.baz[ - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ]; - -const decorated = (arg, ignoreRequestError) => { - return ( - typeof arg === "string" || - (arg && arg.valueOf && typeof arg.valueOf() === "string") - ? $delegate(arg, ignoreRequestError) - : handleAsyncOperations(arg, ignoreRequestError) - ).foo(); -}; - -bifornCringerMoshedPerplexSawder = fn( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -); - -fn( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -) - -bifornCringerMoshedPerplexSawder = fn?.( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -); - -fn?.( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -) - -bifornCringerMoshedPerplexSawder = - fn[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop - ]; - -fn[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -]; - -bifornCringerMoshedPerplexSawder = - fn?.[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop - ]; - -fn?.[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -]; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -255,9 +255,9 @@ - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol --).annularCooeedSplicesWalksWayWay -- .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay) -- .annularCooeedSplicesWalksWayWay(); -+).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( -+ annularCooeedSplicesWalksWayWay, -+).annularCooeedSplicesWalksWayWay(); - - foo = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond -``` - -# Output - -```js -foo7 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)[Fooooooooooo]; - -foo8 = (condition ? firstValue : secondValue)[SomeType]; - -const foo9 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)[Fooooooooooo]; - -function foo10() { - return ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )[Fooooooooooo]; -} - -function foo11() { - throw ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )[Fooooooooooo]; -} - -function foo12() { - void ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )[Fooooooooooo]; -} - -foo13 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -).Fooooooooooo.Fooooooooooo; - -foo14 = (condition ? firstValue : secondValue)[SomeType]; - -const foo15 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -).Fooooooooooo.Fooooooooooo; - -function foo16() { - return ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -function foo17() { - throw ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -function foo18() { - void ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; -} - -foo19 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -foo20 = (condition ? firstValue : secondValue)[SomeType]; - -const foo21 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -function foo22() { - return ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo23() { - throw ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo24() { - void ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -foo25 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)?.(Fooooooooooo.Fooooooooooo); - -foo26 = (condition ? firstValue : secondValue)[SomeType]; - -const foo27 = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)?.(Fooooooooooo.Fooooooooooo); - -function foo28() { - return ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )?.(Fooooooooooo.Fooooooooooo); -} - -function foo29() { - throw ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )?.(Fooooooooooo.Fooooooooooo); -} - -function foo30() { - void ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )?.(Fooooooooooo.Fooooooooooo); -} - -function* foo31() { - yield ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )?.(Fooooooooooo.Fooooooooooo); - yield ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); - yield ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ).Fooooooooooo.Fooooooooooo; - yield ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )[Fooooooooooo.Fooooooooooo]; -} - -const foo32 = new ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -function foo33() { - return new ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo34() { - throw new ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -function foo35() { - void new ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - )(Fooooooooooo.Fooooooooooo); -} - -foo36 = new ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)(Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)[AnnularCooeedSplicesWalksWayWay]; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)[AnnularCooeedSplicesWalksWayWay]; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).Fooooooooooo.Fooooooooooo; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).Fooooooooooo.Fooooooooooo; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol)(Fooooooooooo.Fooooooooooo); - -bifornCringerMoshedPerplexSawder = ( - glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol -).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( - annularCooeedSplicesWalksWayWay, -).annularCooeedSplicesWalksWayWay(); - -foo = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)?.()?.()?.(); - -foo = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)()()(); - -foo = - foo.bar.baz[ - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ]; - -const decorated = (arg, ignoreRequestError) => { - return ( - typeof arg === "string" || - (arg && arg.valueOf && typeof arg.valueOf() === "string") - ? $delegate(arg, ignoreRequestError) - : handleAsyncOperations(arg, ignoreRequestError) - ).foo(); -}; - -bifornCringerMoshedPerplexSawder = fn( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop, -); - -fn( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop, -); - -bifornCringerMoshedPerplexSawder = fn?.( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop, -); - -fn?.( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop, -); - -bifornCringerMoshedPerplexSawder = - fn[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop - ]; - -fn[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -]; - -bifornCringerMoshedPerplexSawder = - fn?.[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop - ]; - -fn?.[ - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).prop -]; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/generic-cast.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/generic-cast.ts.snap deleted file mode 100644 index 88a3c075048..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/generic-cast.ts.snap +++ /dev/null @@ -1,321 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: typescript/cast/generic-cast.ts ---- - -# Input - -```js -// https://github.com/prettier/prettier/issues/4171 -function y() { - - const fits = >fits(); - const fitsObjLiteral = >{ a: "test" }; - const fitsArrayLiteral = >["test", "test2"] - - const breakAfterCast = >someExistingConfigMap.mergeDeep(fallbackOpts); - - const stillTooLong = >someExistingConfigMap.mergeDeep(fallbackOptions); - - const stillTooLong2 = | undefined>someExistingConfigMap.mergeDeep(fallbackOptions); - - const stillTooLong3 = >someExistingConfigMap.mergeDeep(fallbackOptions.someMethodWithLongName(param1, param2)); - - const stillTooLong4 = | undefined>someExistingConfigMap.mergeDeep(fallbackOptions.someMethodWithLongName(param1, param2)); - - const testObjLiteral = >{ property1: "myPropertyVal" }; - - const testObjLiteral2 = >{ property1: "myPropertyVal" }; - - const testArrayLiteral = >["first", "second", "third"]; - - const testArrayLiteral2 = >["first", "second", "third"]; - - const insideFuncCall = myFunc(param1, >param2, param3) -} - -// https://github.com/prettier/prettier/issues/4168 -function x() { - const fits = | undefined>(permissions)[type]; - const fitsObjLiteral = | undefined>{ a: "test" }; - const fitsArrayLiteral = | undefined>["t1", "t2"]; - - const breakAfterCast = | undefined>(permissions)[receiverType]; - - const stillTooLong = | undefined | number | string | boolean>(permissions)[receiverType]; - - const stillTooLong2 = | undefined | number | string | boolean | null | never>(permissions)[receiverType]; - - const stillTooLong3 = | undefined>(permissions.someMethodWithLongName(parameter1, parameter2))[receiverTypeLongName]; - - const stillTooLong4 = | undefined | number | string | boolean | null | never>(permissions.someMethodWithLongName(parameter1, parameter2))[receiverTypeLongName]; - - const testObjLiteral = | undefined>{ prop1: "myPropVal" }; - - const testObjLiteral2 = | undefined | number | string | boolean | null | never | object>{ prop1: "myPropVal" }; - - const testArrayLiteral = | undefined>["first", "second", "third"]; - - const testArrayLiteral2 = | undefined | number | string | boolean | null | never | object>["first", "second", "third"]; - - const insideFuncCall = myFunc(param1, | undefined>param2, param3) -} -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -4,9 +4,9 @@ - const fitsObjLiteral = >{ a: "test" }; - const fitsArrayLiteral = >["test", "test2"]; - -- const breakAfterCast = >( -- someExistingConfigMap.mergeDeep(fallbackOpts) -- ); -+ const breakAfterCast = < -+ Immutable.Map -+ >someExistingConfigMap.mergeDeep(fallbackOpts); - - const stillTooLong = < - Immutable.Map< -@@ -37,10 +37,8 @@ - | undefined - >someExistingConfigMap.mergeDeep(fallbackOptions); - -- const stillTooLong3 = >( -- someExistingConfigMap.mergeDeep( -- fallbackOptions.someMethodWithLongName(param1, param2), -- ) -+ const stillTooLong3 = >someExistingConfigMap.mergeDeep( -+ fallbackOptions.someMethodWithLongName(param1, param2), - ); - - const stillTooLong4 = < -@@ -111,9 +109,9 @@ - const fitsObjLiteral = | undefined>{ a: "test" }; - const fitsArrayLiteral = | undefined>["t1", "t2"]; - -- const breakAfterCast = | undefined>( -- (permissions)[receiverType] -- ); -+ const breakAfterCast = | undefined>(< -+ any -+ >permissions)[receiverType]; - - const stillTooLong = < - PermissionsChecker | undefined | number | string | boolean -@@ -129,11 +127,11 @@ - | never - >(permissions)[receiverType]; - -- const stillTooLong3 = | undefined>( -- (permissions.someMethodWithLongName(parameter1, parameter2))[ -- receiverTypeLongName -- ] -- ); -+ const stillTooLong3 = | undefined>(< -+ any -+ >permissions.someMethodWithLongName(parameter1, parameter2))[ -+ receiverTypeLongName -+ ]; - - const stillTooLong4 = < - | PermissionsChecker -``` - -# Output - -```js -// https://github.com/prettier/prettier/issues/4171 -function y() { - const fits = >fits(); - const fitsObjLiteral = >{ a: "test" }; - const fitsArrayLiteral = >["test", "test2"]; - - const breakAfterCast = < - Immutable.Map - >someExistingConfigMap.mergeDeep(fallbackOpts); - - const stillTooLong = < - Immutable.Map< - string, - boolean, - number, - object, - null, - undefined, - any, - void, - never - > - >someExistingConfigMap.mergeDeep(fallbackOptions); - - const stillTooLong2 = < - | Immutable.Map< - string, - boolean, - number, - object, - null, - undefined, - any, - void, - never - > - | undefined - >someExistingConfigMap.mergeDeep(fallbackOptions); - - const stillTooLong3 = >someExistingConfigMap.mergeDeep( - fallbackOptions.someMethodWithLongName(param1, param2), - ); - - const stillTooLong4 = < - | Immutable.Map< - string, - boolean, - number, - object, - null, - undefined, - any, - void, - never - > - | undefined - >someExistingConfigMap.mergeDeep( - fallbackOptions.someMethodWithLongName(param1, param2), - ); - - const testObjLiteral = >{ - property1: "myPropertyVal", - }; - - const testObjLiteral2 = < - Immutable.Map< - string, - any, - number, - boolean, - object, - null, - undefined, - never, - "extra long" - > - >{ property1: "myPropertyVal" }; - - const testArrayLiteral = >[ - "first", - "second", - "third", - ]; - - const testArrayLiteral2 = < - Immutable.Map< - string, - any, - number, - boolean, - object, - null, - undefined, - never, - "extra long" - > - >["first", "second", "third"]; - - const insideFuncCall = myFunc( - param1, - >param2, - param3, - ); -} - -// https://github.com/prettier/prettier/issues/4168 -function x() { - const fits = | undefined>(permissions)[type]; - const fitsObjLiteral = | undefined>{ a: "test" }; - const fitsArrayLiteral = | undefined>["t1", "t2"]; - - const breakAfterCast = | undefined>(< - any - >permissions)[receiverType]; - - const stillTooLong = < - PermissionsChecker | undefined | number | string | boolean - >(permissions)[receiverType]; - - const stillTooLong2 = < - | PermissionsChecker - | undefined - | number - | string - | boolean - | null - | never - >(permissions)[receiverType]; - - const stillTooLong3 = | undefined>(< - any - >permissions.someMethodWithLongName(parameter1, parameter2))[ - receiverTypeLongName - ]; - - const stillTooLong4 = < - | PermissionsChecker - | undefined - | number - | string - | boolean - | null - | never - >(permissions.someMethodWithLongName(parameter1, parameter2))[ - receiverTypeLongName - ]; - - const testObjLiteral = | undefined>{ - prop1: "myPropVal", - }; - - const testObjLiteral2 = < - | PermissionsChecker - | undefined - | number - | string - | boolean - | null - | never - | object - >{ prop1: "myPropVal" }; - - const testArrayLiteral = | undefined>[ - "first", - "second", - "third", - ]; - - const testArrayLiteral2 = < - | PermissionsChecker - | undefined - | number - | string - | boolean - | null - | never - | object - >["first", "second", "third"]; - - const insideFuncCall = myFunc( - param1, - | undefined>param2, - param3, - ); -} -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/custom/typeParameters/variables.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/custom/typeParameters/variables.ts.snap deleted file mode 100644 index 57957fc60a4..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/custom/typeParameters/variables.ts.snap +++ /dev/null @@ -1,87 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs -info: - test_file: typescript/custom/typeParameters/variables.ts ---- - -# Input - -```js -const foo: SomeThing = func(); -const bar: SomeThing = func(); -const fooo: SomeThing<{ [P in "x" | "y"]: number }> = func(); -const baar: SomeThing = func(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaaaaaaaar: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaar: SomeThing<{ [P in "x" | "y"]: number }> = looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaaar: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const isAnySuccessfulAttempt$: Observable = this._quizService.isAnySuccessfulAttempt$().pipe( - tap((isAnySuccessfulAttempt: boolean) => { - this.isAnySuccessfulAttempt = isAnySuccessfulAttempt; - }), -); -const isAnySuccessfulAttempt2$: Observable = this._someMethodWithLongName(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = looooooooooooooooooooooooooooooongNameFunc(); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -10,9 +10,8 @@ - looooooooooooooooooooooooooooooongNameFunc(); - const baaaaaaaaaaaaaaaar: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); --const isAnySuccessfulAttempt$: Observable = this._quizService -- .isAnySuccessfulAttempt$() -- .pipe( -+const isAnySuccessfulAttempt$: Observable = -+ this._quizService.isAnySuccessfulAttempt$().pipe( - tap((isAnySuccessfulAttempt: boolean) => { - this.isAnySuccessfulAttempt = isAnySuccessfulAttempt; - }), -``` - -# Output - -```js -const foo: SomeThing = func(); -const bar: SomeThing = func(); -const fooo: SomeThing<{ [P in "x" | "y"]: number }> = func(); -const baar: SomeThing = func(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaaaaaaaar: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaar: SomeThing<{ [P in "x" | "y"]: number }> = - looooooooooooooooooooooooooooooongNameFunc(); -const baaaaaaaaaaaaaaaar: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const isAnySuccessfulAttempt$: Observable = - this._quizService.isAnySuccessfulAttempt$().pipe( - tap((isAnySuccessfulAttempt: boolean) => { - this.isAnySuccessfulAttempt = isAnySuccessfulAttempt; - }), - ); -const isAnySuccessfulAttempt2$: Observable = - this._someMethodWithLongName(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -const fooooooooooooooo: SomeThing = - looooooooooooooooooooooooooooooongNameFunc(); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/ternaries/indent.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/ternaries/indent.ts.snap deleted file mode 100644 index a448b013054..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/ternaries/indent.ts.snap +++ /dev/null @@ -1,160 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -foo = (callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression; - -foo = (callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression; - -bifornCringerMoshedPerplexSawder = (glimseGlyphsHazardNoopsTieTie === 0 && -kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol -).annularCooeedSplicesWalksWayWay - .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay)! - .annularCooeedSplicesWalksWayWay(); - -foo = (callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression!; - -foo = (callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression!; - -bifornCringerMoshedPerplexSawder = (glimseGlyphsHazardNoopsTieTie === 0 && -kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol -).annularCooeedSplicesWalksWayWay - .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay)! - .annularCooeedSplicesWalksWayWay()!; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).Foo!.foo; - -foo = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)!; - -foo = (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz)!!!!!; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -15,9 +15,9 @@ - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol --).annularCooeedSplicesWalksWayWay -- .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay)! -- .annularCooeedSplicesWalksWayWay(); -+).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( -+ annularCooeedSplicesWalksWayWay, -+)!.annularCooeedSplicesWalksWayWay(); - - foo = ( - callNode.parent?.type === AST_NODE_TYPES.ChainExpression -@@ -36,9 +36,9 @@ - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol --).annularCooeedSplicesWalksWayWay -- .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay)! -- .annularCooeedSplicesWalksWayWay()!; -+).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( -+ annularCooeedSplicesWalksWayWay, -+)!.annularCooeedSplicesWalksWayWay()!; - - bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + -``` - -# Output - -```js -foo = ( - callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression; - -foo = ( - callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression; - -bifornCringerMoshedPerplexSawder = ( - glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol -).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( - annularCooeedSplicesWalksWayWay, -)!.annularCooeedSplicesWalksWayWay(); - -foo = ( - callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression!; - -foo = ( - callNode.parent?.type === AST_NODE_TYPES.ChainExpression - ? callNode.parent.parent - : callNode.parent -).TSESTree!.BinaryExpression!; - -bifornCringerMoshedPerplexSawder = ( - glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol -).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay( - annularCooeedSplicesWalksWayWay, -)!.annularCooeedSplicesWalksWayWay()!; - -bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol - ).Foo!.foo; - -foo = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)!; - -foo = ( - coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz -)!!!!!; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/typeparams/class-method.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/typeparams/class-method.ts.snap index a98f975cc32..67d479287b2 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/typeparams/class-method.ts.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/typescript/typeparams/class-method.ts.snap @@ -1,5 +1,7 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs +info: + test_file: typescript/typeparams/class-method.ts --- # Input @@ -110,21 +112,7 @@ const appIDs = createSelector( ```diff --- Prettier +++ Rome -@@ -54,15 +54,19 @@ - export class Thing8 implements OtherThing { - do: (type: Type) => Provider = memoize( - (type: ObjectType) => -- >( -- type.doSomething(withArgs, soIt, does, not, fit).extraCall() -- ), -+ >type.doSomething( -+ withArgs, -+ soIt, -+ does, -+ not, -+ fit, -+ ).extraCall(), - ); +@@ -61,8 +61,8 @@ } export class Thing9 implements OtherThing { @@ -135,23 +123,6 @@ const appIDs = createSelector( ); } -@@ -143,9 +147,13 @@ - - export class Thing19 implements OtherThing { - do: (type: Type) => Provider = memoize(function (type: ObjectType) { -- return >( -- type.doSomething(withArgs, soIt, does, not, fit).extraCall() -- ); -+ return >type.doSomething( -+ withArgs, -+ soIt, -+ does, -+ not, -+ fit, -+ ).extraCall(); - }); - } - ``` # Output @@ -213,13 +184,9 @@ export class Thing7 implements OtherThing { export class Thing8 implements OtherThing { do: (type: Type) => Provider = memoize( (type: ObjectType) => - >type.doSomething( - withArgs, - soIt, - does, - not, - fit, - ).extraCall(), + >( + type.doSomething(withArgs, soIt, does, not, fit).extraCall() + ), ); } @@ -306,13 +273,9 @@ export class Thing18 implements OtherThing { export class Thing19 implements OtherThing { do: (type: Type) => Provider = memoize(function (type: ObjectType) { - return >type.doSomething( - withArgs, - soIt, - does, - not, - fit, - ).extraCall(); + return >( + type.doSomething(withArgs, soIt, does, not, fit).extraCall() + ); }); } diff --git a/editors/vscode/src/commands/syntaxTree.ts b/editors/vscode/src/commands/syntaxTree.ts index 17763a1cc2f..0931af91b55 100644 --- a/editors/vscode/src/commands/syntaxTree.ts +++ b/editors/vscode/src/commands/syntaxTree.ts @@ -92,16 +92,14 @@ class SyntaxTreeProvider }; // send request to the server and store its content in the cache if successful - return this.session.client.sendRequest( - syntaxTreeRequest, - params, - token, - ).then((result) => { - const document = new SyntaxTreeDocument(uri, result); - this.documents.set(documentUri, document); - - return document.value; - }); + return this.session.client + .sendRequest(syntaxTreeRequest, params, token) + .then((result) => { + const document = new SyntaxTreeDocument(uri, result); + this.documents.set(documentUri, document); + + return document.value; + }); } dispose(): any { diff --git a/npm/backend-jsonrpc/src/command.ts b/npm/backend-jsonrpc/src/command.ts index 06b1edeb735..bc45faf27f7 100644 --- a/npm/backend-jsonrpc/src/command.ts +++ b/npm/backend-jsonrpc/src/command.ts @@ -1,6 +1,6 @@ /** * Gets the path of the Rome binary for the current platform - * + * * @returns Filesystem path to the binary, or null if no prebuilt distribution exists for the current platform */ export function getCommand(): string | null { diff --git a/npm/backend-jsonrpc/src/index.ts b/npm/backend-jsonrpc/src/index.ts index fa10aa17c4b..891221669d2 100644 --- a/npm/backend-jsonrpc/src/index.ts +++ b/npm/backend-jsonrpc/src/index.ts @@ -1,12 +1,16 @@ import { getCommand } from "./command"; import { createSocket } from "./socket"; import { Transport } from "./transport"; -import { createWorkspace as wrapTransport, type Workspace, type RomePath } from "./workspace"; +import { + createWorkspace as wrapTransport, + type Workspace, + type RomePath, +} from "./workspace"; /** * Create an instance of the Workspace client connected to a remote daemon * instance through the JSON-RPC protocol - * + * * @returns A Workspace client, or null if the underlying platform is not supported */ export async function createWorkspace(): Promise { @@ -22,7 +26,7 @@ export async function createWorkspace(): Promise { * Create an instance of the Workspace client connected to a remote daemon * instance through the JSON-RPC protocol, using the provided command to spawn * the daemon if necessary - * + * * @param command Path to the Rome binary distribution * @returns A Workspace client, or null if the underlying platform is not supported */ diff --git a/npm/backend-jsonrpc/src/socket.ts b/npm/backend-jsonrpc/src/socket.ts index b5204d5cf7e..b5081619ae3 100644 --- a/npm/backend-jsonrpc/src/socket.ts +++ b/npm/backend-jsonrpc/src/socket.ts @@ -18,7 +18,11 @@ function getSocket(command: string): Promise { if (code === 0) { resolve(pipeName.trimEnd()); } else { - reject(new Error(`Command '${command} __print_socket' exited with code ${code}`)); + reject( + new Error( + `Command '${command} __print_socket' exited with code ${code}`, + ), + ); } }); }); diff --git a/npm/backend-jsonrpc/src/transport.ts b/npm/backend-jsonrpc/src/transport.ts index 69bd8c09128..c1f070d5f22 100644 --- a/npm/backend-jsonrpc/src/transport.ts +++ b/npm/backend-jsonrpc/src/transport.ts @@ -59,15 +59,15 @@ function isJsonRpcNotification( type JsonRpcResponse = | { - jsonrpc: "2.0"; - id: number; - result: any; - } + jsonrpc: "2.0"; + id: number; + result: any; + } | { - jsonrpc: "2.0"; - id: number; - error: any; - }; + jsonrpc: "2.0"; + id: number; + error: any; + }; function isJsonRpcResponse( message: JsonRpcMessage, @@ -118,7 +118,7 @@ export class Transport { /** * Send a request to the remote server - * + * * @param method Name of the remote method to call * @param params Parameters object the remote method should be called with * @return Promise resolving with the value returned by the remote method, or rejecting with an RPC error if the remote call failed @@ -138,7 +138,7 @@ export class Transport { /** * Send a notification message to the remote server - * + * * @param method Name of the remote method to call * @param params Parameters object the remote method should be called with */ diff --git a/npm/backend-jsonrpc/tests/transport.test.mjs b/npm/backend-jsonrpc/tests/transport.test.mjs index 2776cda3bdd..69ccbb8f675 100644 --- a/npm/backend-jsonrpc/tests/transport.test.mjs +++ b/npm/backend-jsonrpc/tests/transport.test.mjs @@ -6,9 +6,9 @@ function makeMessage(body) { const content = JSON.stringify(body); return Buffer.from( `Content-Length: ${content.length}\r\n` + - `Content-Type: application/vscode-jsonrpc;charset=utf-8\r\n` + - `\r\n` + - content + `Content-Type: application/vscode-jsonrpc;charset=utf-8\r\n` + + `\r\n` + + content, ); } @@ -17,7 +17,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -26,20 +26,24 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - const result = transport.request('method', "params"); - - expect(socket.write).toHaveBeenCalledWith(makeMessage({ - jsonrpc: "2.0", - id: 0, - method: "method", - params: "params", - })); - - onData(makeMessage({ - jsonrpc: "2.0", - id: 0, - result: "result", - })); + const result = transport.request("method", "params"); + + expect(socket.write).toHaveBeenCalledWith( + makeMessage({ + jsonrpc: "2.0", + id: 0, + method: "method", + params: "params", + }), + ); + + onData( + makeMessage({ + jsonrpc: "2.0", + id: 0, + result: "result", + }), + ); const response = await result; expect(response).toMatchObject({}); @@ -52,7 +56,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -61,7 +65,9 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - expect(() => onData(Buffer.from(`\r\n`))).toThrowError('incoming message from the remote workspace is missing the Content-Length header'); + expect(() => onData(Buffer.from(`\r\n`))).toThrowError( + "incoming message from the remote workspace is missing the Content-Length header", + ); transport.destroy(); expect(socket.destroy).toHaveBeenCalledOnce(); @@ -71,7 +77,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -80,7 +86,9 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - expect(() => onData(Buffer.from(`Content-Length\r\n`))).toThrowError('could not find colon token in "Content-Length\r\n"'); + expect(() => onData(Buffer.from(`Content-Length\r\n`))).toThrowError( + 'could not find colon token in "Content-Length\r\n"', + ); transport.destroy(); expect(socket.destroy).toHaveBeenCalledOnce(); @@ -90,7 +98,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -99,7 +107,11 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - expect(() => onData(Buffer.from(`Content-Type: text/plain\r\n`))).toThrowError('invalid value for Content-Type expected "application/vscode-jsonrpc", got "text/plain"'); + expect( + () => onData(Buffer.from(`Content-Type: text/plain\r\n`)), + ).toThrowError( + 'invalid value for Content-Type expected "application/vscode-jsonrpc", got "text/plain"', + ); transport.destroy(); expect(socket.destroy).toHaveBeenCalledOnce(); @@ -109,7 +121,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -118,7 +130,11 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - expect(() => onData(makeMessage({ jsonrpc: "2.0", id: 0, result: "result" }))).toThrowError('could not find any pending request matching RPC response ID 0'); + expect( + () => onData(makeMessage({ jsonrpc: "2.0", id: 0, result: "result" })), + ).toThrowError( + "could not find any pending request matching RPC response ID 0", + ); transport.destroy(); expect(socket.destroy).toHaveBeenCalledOnce(); @@ -128,7 +144,7 @@ describe("Transport Layer", () => { let onData = null; const socket = { on(event, fn) { - expect(event).toBe('data'); + expect(event).toBe("data"); onData = fn; }, write: vi.fn(), @@ -137,7 +153,9 @@ describe("Transport Layer", () => { const transport = new Transport(socket); - expect(() => onData(makeMessage({}))).toThrowError('failed to deserialize incoming message from remote workspace, "{}" is not a valid JSON-RPC message body'); + expect(() => onData(makeMessage({}))).toThrowError( + 'failed to deserialize incoming message from remote workspace, "{}" is not a valid JSON-RPC message body', + ); transport.destroy(); expect(socket.destroy).toHaveBeenCalledOnce(); diff --git a/npm/backend-jsonrpc/tests/workspace.test.mjs b/npm/backend-jsonrpc/tests/workspace.test.mjs index 15b829abd6f..77f7bdf7582 100644 --- a/npm/backend-jsonrpc/tests/workspace.test.mjs +++ b/npm/backend-jsonrpc/tests/workspace.test.mjs @@ -12,9 +12,9 @@ describe("Workspace API", () => { "../../../..", `target/release/rome${extension}`, ); - + const workspace = await createWorkspaceWithBinary(command); - + await workspace.openFile({ path: { path: "test.js", @@ -23,7 +23,7 @@ describe("Workspace API", () => { content: "statement()", version: 0, }); - + const printed = await workspace.formatFile({ path: { path: "test.js", @@ -32,14 +32,14 @@ describe("Workspace API", () => { }); expect(printed.code).toBe("statement();\n"); - + await workspace.closeFile({ path: { path: "test.js", id: 0, }, }); - + workspace.destroy(); }); }); diff --git a/website/.eleventy.js b/website/.eleventy.js index 3b0a02b3000..34eb5e2b12e 100644 --- a/website/.eleventy.js +++ b/website/.eleventy.js @@ -60,10 +60,11 @@ module.exports = function (eleventyConfig) { permalinkAttrs: (slug) => ({ "aria-label": slug }), slugify: (title) => { return encodeURIComponent( - String(title).trim().toLowerCase().replace( - /[^a-zA-Z\s0-9]/g, - "", - ).replace(/\s+/g, "-"), + String(title) + .trim() + .toLowerCase() + .replace(/[^a-zA-Z\s0-9]/g, "") + .replace(/\s+/g, "-"), ); }, }); @@ -171,9 +172,11 @@ module.exports = function (eleventyConfig) { eleventyConfig.addFilter("blogSummary", (val) => { const lines = val.split("")[0].split("\n"); - return lines.filter((line) => { - return line.startsWith("

"); - }).join("\n"); + return lines + .filter((line) => { + return line.startsWith("

"); + }) + .join("\n"); }); eleventyConfig.addFilter("dateFormat", function (value) {