diff --git a/Cargo.lock b/Cargo.lock index 1fcc82576f2..07edc4c1aee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1584,6 +1584,7 @@ dependencies = [ "cfg-if", "indexmap", "rome_rowan", + "rustc-hash", "schemars", "serde", "tracing", diff --git a/crates/rome_formatter/Cargo.toml b/crates/rome_formatter/Cargo.toml index dedcfc884af..c288a1f6035 100644 --- a/crates/rome_formatter/Cargo.toml +++ b/crates/rome_formatter/Cargo.toml @@ -15,6 +15,7 @@ serde = { version = "1.0.136", features = ["derive"], optional = true } cfg-if = "1.0.0" indexmap = "1.8.2" schemars = { version = "0.8.10", optional = true } +rustc-hash = "1.1.0" [features] serde = ["dep:serde", "schemars", "rome_rowan/serde"] diff --git a/crates/rome_formatter/src/format_element.rs b/crates/rome_formatter/src/format_element.rs index a9e14a63350..5ede04a8129 100644 --- a/crates/rome_formatter/src/format_element.rs +++ b/crates/rome_formatter/src/format_element.rs @@ -4,6 +4,7 @@ use crate::{ format, format_args, group, soft_block_indent, soft_line_break_or_space, soft_line_indent_or_space, space, text, write, Buffer, Format, FormatContext, FormatOptions, FormatResult, Formatter, GroupId, IndentStyle, LineWidth, PrinterOptions, TextSize, + TransformSourceMap, }; use indexmap::IndexSet; #[cfg(target_pointer_width = "64")] @@ -771,6 +772,10 @@ impl FormatContext for IrFormatContext { fn options(&self) -> &Self::Options { &IrFormatOptions } + + fn source_map(&self) -> Option<&TransformSourceMap> { + None + } } #[derive(Debug, Clone, Default)] diff --git a/crates/rome_formatter/src/lib.rs b/crates/rome_formatter/src/lib.rs index 1d51260165a..6b79acfa71c 100644 --- a/crates/rome_formatter/src/lib.rs +++ b/crates/rome_formatter/src/lib.rs @@ -35,6 +35,7 @@ pub mod prelude; #[cfg(debug_assertions)] pub mod printed_tokens; pub mod printer; +mod source_map; pub mod token; use crate::formatter::Formatter; @@ -63,6 +64,7 @@ use rome_rowan::{ Language, RawSyntaxKind, SyntaxElement, SyntaxError, SyntaxKind, SyntaxNode, SyntaxResult, SyntaxToken, SyntaxTriviaPieceComments, TextRange, TextSize, TokenAtOffset, }; +pub use source_map::{TransformSourceMap, TransformSourceMapBuilder}; use std::error::Error; use std::num::ParseIntError; use std::str::FromStr; @@ -206,6 +208,13 @@ pub trait FormatContext { /// Returns the formatting options fn options(&self) -> &Self::Options; + + /// Returns [None] if the CST has not been pre-processed. + /// + /// Returns [Some] if the CST has been pre-processed to simplify formatting. + /// The source map can be used to map positions of the formatted nodes back to their original + /// source locations or to resolve the source text. + fn source_map(&self) -> Option<&TransformSourceMap>; } /// Options customizing how the source code should be formatted. @@ -253,6 +262,10 @@ impl FormatContext for SimpleFormatContext { fn options(&self) -> &Self::Options { &self.options } + + fn source_map(&self) -> Option<&TransformSourceMap> { + None + } } #[derive(Debug, Default, Eq, PartialEq)] @@ -278,7 +291,7 @@ impl FormatOptions for SimpleFormatOptions { } /// Lightweight sourcemap marker between source and output tokens -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema) @@ -318,12 +331,23 @@ where { pub fn print(&self) -> Printed { let print_options = self.context.options().as_print_options(); - Printer::new(print_options).print(&self.root) + + let printed = Printer::new(print_options).print(&self.root); + + match self.context.source_map() { + Some(source_map) => source_map.map_printed(printed), + None => printed, + } } pub fn print_with_indent(&self, indent: u16) -> Printed { let print_options = self.context.options().as_print_options(); - Printer::new(print_options).print_with_indent(&self.root, indent) + let printed = Printer::new(print_options).print_with_indent(&self.root, indent); + + match self.context.source_map() { + Some(source_map) => source_map.map_printed(printed), + None => printed, + } } } @@ -376,7 +400,8 @@ impl Printed { } /// Returns a list of [SourceMarker] mapping byte positions - /// in the output string to the input source code + /// in the output string to the input source code. + /// It's not guaranteed that the markers are sorted by source position. pub fn sourcemap(&self) -> &[SourceMarker] { &self.sourcemap } @@ -796,10 +821,7 @@ where buffer.write_fmt(arguments)?; - Ok(Formatted { - root: buffer.into_element(), - context: state.into_context(), - }) + Ok(Formatted::new(buffer.into_element(), state.into_context())) } /// Entry point for formatting a [SyntaxNode] for a specific language. @@ -818,6 +840,18 @@ pub trait FormatLanguage { /// Customizes how comments are formatted fn comment_style(&self) -> Self::CommentStyle; + /// Performs an optional pre-processing of the tree. This can be useful to remove nodes + /// that otherwise complicate formatting. + /// + /// Return [None] if the tree shouldn't be processed. Return [Some] with the transformed + /// tree and the source map otherwise. + fn transform( + &self, + _root: &SyntaxNode, + ) -> Option<(SyntaxNode, TransformSourceMap)> { + None + } + /// This is used to select appropriate "root nodes" for the /// range formatting process: for instance in JavaScript the function returns /// true for statement and declaration nodes, to ensure the entire statement @@ -830,7 +864,11 @@ pub trait FormatLanguage { fn options(&self) -> &::Options; /// Creates the [FormatContext] with the given `source map` and `comments` - fn create_context(self, comments: Comments) -> Self::Context; + fn create_context( + self, + comments: Comments, + source_map: Option, + ) -> Self::Context; } /// Formats a syntax node file based on its features. @@ -841,22 +879,27 @@ pub fn format_node( language: L, ) -> FormatResult> { tracing::trace_span!("format_node").in_scope(move || { - let comments = Comments::from_node(root, &language); - let format_node = FormatRefWithRule::new(root, L::FormatRule::default()); + let (root, source_map) = match language.transform(root) { + Some((root, source_map)) => (root, Some(source_map)), + None => (root.clone(), None), + }; - let context = language.create_context(comments); + let comments = Comments::from_node(&root, &language); + let format_node = FormatRefWithRule::new(&root, L::FormatRule::default()); + + let context = language.create_context(comments, source_map); let mut state = FormatState::new(context); let mut buffer = VecBuffer::new(&mut state); - write!(&mut buffer, [format_node])?; + write!(buffer, [format_node])?; let document = buffer.into_element(); - state.assert_formatted_all_tokens(root); + state.assert_formatted_all_tokens(&root); state .context() .comments() - .assert_checked_all_suppressions(root); + .assert_checked_all_suppressions(&root); Ok(Formatted::new(document, state.into_context())) }) @@ -1198,6 +1241,7 @@ pub fn format_sub_tree( let mut printed = formatted.print_with_indent(initial_indent); let sourcemap = printed.take_sourcemap(); let verbatim_ranges = printed.take_verbatim_ranges(); + Ok(Printed::new( printed.into_code(), Some(root.text_range()), @@ -1226,9 +1270,9 @@ impl Format for SyntaxTriviaPieceComments { /// This structure is different from [crate::Formatter] in that the formatting infrastructure /// creates a new [crate::Formatter] for every [crate::write!] call, whereas this structure stays alive /// for the whole process of formatting a root with [crate::format!]. -#[derive(Default)] pub struct FormatState { context: Context, + group_id_builder: UniqueGroupIdBuilder, /// `true` if the last formatted output is an inline comment that may need a space between the next token or comment. diff --git a/crates/rome_formatter/src/source_map.rs b/crates/rome_formatter/src/source_map.rs new file mode 100644 index 00000000000..0afc76b8925 --- /dev/null +++ b/crates/rome_formatter/src/source_map.rs @@ -0,0 +1,613 @@ +use crate::{Printed, SourceMarker, TextRange}; +use rome_rowan::{Language, SyntaxNode, SyntaxNodeText, TextSize}; +use rustc_hash::FxHashMap; +use std::cmp::Ordering; + +/// A source map for mapping positions of a pre-processed tree back to the locations in the source tree. +/// +/// This is not a generic purpose source map but instead focused on supporting the case where +/// a language removes or re-orders nodes that would otherwise complicate the formatting logic. +/// A common use case for pre-processing is the removal of all parenthesized nodes. +/// Removing parenthesized nodes simplifies the formatting logic when it has different behaviour +/// depending if a child or parent is of a specific node kind. Performing such a test with parenthesized +/// nodes present in the source code means that the formatting logic has to skip over all parenthesized nodes +/// until it finds the first non-parenthesized node and then test if that node is of the expected kind. +/// +/// This source map implementation supports removing tokens or re-structuring nodes +/// without changing the order of the tokens in the tree (requires no source map). +/// +/// The following section uses parentheses as a concrete example to explain the functionality of the source map. +/// However, the source map implementation isn't restricted to removing parentheses only, it supports mapping +/// transformed to source position for any use case where a transform deletes text from the source tree. +/// +/// ## Position Mapping +/// +/// The source map internally tracks all the ranges that have been deleted from the source code sorted by the start of the deleted range. +/// It further stores the absolute count of deleted bytes preceding a range. The deleted range together +/// with the absolute count allows to re-compute the source location for every transformed location +/// and has the benefit that it requires significantly fewer memory +/// than source maps that use a source to destination position marker for every token. +/// +/// ## Map Node Ranges +/// +/// Only having the deleted ranges to resolve the original text of a node isn't sufficient. +/// Resolving the original text of a node is needed when formatting a node as verbatim, either because +/// formatting the node failed because of a syntax error, or formatting is suppressed with a `rome-ignore format:` comment. +/// +/// ```text +/// // Source // Transformed +/// (a+b) + (c + d) a + b + c + d; +/// ``` +/// +/// Using the above example, the following source ranges should be returned when quering with the transformed ranges: +/// +/// * `a` -> `a`: Should not include the leading `(` +/// * `b` -> `b`: Should not include the trailing `)` +/// * `a + b` -> `(a + b)`: Should include the leading `(` and trailing `)`. +/// * `a + b + c + d` -> `(a + b) + (c + d)`: Should include the fist `(` token and the last `)` token because the expression statement +/// fully encloses the `a + b` and `c + d` nodes. +/// +/// This is why the source map also tracks the mapped trimmed ranges for every node. +#[derive(Debug, Clone)] +pub struct TransformSourceMap { + source_text: SyntaxNodeText, + + /// The mappings stored in increasing order + deleted_ranges: Vec, + + /// Key: Start or end position of node for which the trimmed range should be extended + /// Value: The trimmed range. + mapped_node_ranges: FxHashMap, +} + +impl TransformSourceMap { + /// Returns the text of the source document as it was before the transformation. + pub fn text(&self) -> &SyntaxNodeText { + &self.source_text + } + + /// Maps a range of the transformed document to a range in the source document. + /// + /// Complexity: `O(log(n))` + pub fn source_range(&self, transformed_range: TextRange) -> TextRange { + let range = TextRange::new( + self.source_offset(transformed_range.start(), RangePosition::Start), + self.source_offset(transformed_range.end(), RangePosition::End), + ); + + debug_assert!(range.end() <= self.source_text.len(), "Mapped range {:?} exceeds the length of the source document {:?}. Please check if the passed `transformed_range` is a range of the transformed tree and not of the source tree, and that it belongs to the tree for which the source map was created for.", range, self.source_text.len()); + range + } + + /// Maps the trimmed range of the transformed node to the trimmed range in the source document. + /// + /// Average Complexity: `O(log(n))` + pub fn trimmed_source_range(&self, node: &SyntaxNode) -> TextRange { + self.trimmed_source_range_from_transformed_range(node.text_trimmed_range()) + } + + fn trimmed_source_range_from_transformed_range( + &self, + transformed_range: TextRange, + ) -> TextRange { + let source_range = self.source_range(transformed_range); + + let mut mapped_range = source_range; + + loop { + let mut widened = false; + + let start_mapping = self.mapped_node_ranges.get(&mapped_range.start()); + if let Some(mapping) = start_mapping { + // If the queried node fully encloses the original range of the node, then extend the range + if mapped_range.contains_range(mapping.original_range) { + mapped_range = + TextRange::new(mapping.extended_range.start(), mapped_range.end()); + widened = true; + } + } + + let end_mapping = self.mapped_node_ranges.get(&mapped_range.end()); + if let Some(mapping) = end_mapping { + // If the queried node fully encloses the original range of the node, then extend the range + if mapped_range.contains_range(mapping.original_range) { + mapped_range = + TextRange::new(mapped_range.start(), mapping.extended_range.end()); + widened = true; + } + } + + if !widened { + break; + } + } + + mapped_range + } + + /// Returns the source text of the trimmed range of `node`. + pub fn trimmed_source_text(&self, node: &SyntaxNode) -> SyntaxNodeText { + let range = self.trimmed_source_range(node); + self.source_text.slice(range) + } + + #[cfg(test)] + fn trimmed_source_text_from_transformed_range(&self, range: TextRange) -> SyntaxNodeText { + let range = self.trimmed_source_range_from_transformed_range(range); + self.source_text.slice(range) + } + + fn source_offset(&self, transformed_offset: TextSize, position: RangePosition) -> TextSize { + let index = self + .deleted_ranges + .binary_search_by_key(&transformed_offset, |range| range.transformed_start()); + + let range = match index { + Ok(index) => Some(&self.deleted_ranges[index]), + Err(index) => { + if index == 0 { + None + } else { + self.deleted_ranges.get(index - 1) + } + } + }; + + self.source_offset_with_range(transformed_offset, position, range) + } + + fn source_offset_with_range( + &self, + transformed_offset: TextSize, + position: RangePosition, + deleted_range: Option<&DeletedRange>, + ) -> TextSize { + match deleted_range { + Some(range) => { + debug_assert!( + range.transformed_start() <= transformed_offset, + "Transformed start {:?} must be less than or equal to transformed offset {:?}.", + range.transformed_start(), + transformed_offset + ); + // Transformed position directly falls onto a position where a deleted range starts or ends (depending on the position) + // For example when querying: `a` in `(a)` or (a + b)`, or `b` + if range.transformed_start() == transformed_offset { + match position { + RangePosition::Start => range.source_end(), + // `a)`, deleted range is right after the token. That's why `source_start` is the offset + // that truncates the `)` and `source_end` includes it + RangePosition::End => range.source_start(), + } + } + // The position falls outside of a position that has a leading/trailing deleted range. + // For example, if you get the position of `+` in `(a + b)`. + // That means, the trimmed and non-trimmed offsets are the same + else { + let transformed_delta = transformed_offset - range.transformed_start(); + range.source_start() + range.len() + transformed_delta + } + } + None => transformed_offset, + } + } + + /// Maps the source code positions relative to the transformed tree of `printed` to the location + /// in the original, untransformed source code. + /// + /// The printer creates a source map that allows mapping positions from the newly formatted document + /// back to the locations of the tree. However, the source positions stored in [crate::FormatElement::Text] + /// are relative to the transformed tree and not the original tree passed to [crate::format_node]. + /// + /// This function re-maps the positions from the positions in the transformed tree back to the positions + /// in the original, untransformed tree. + pub fn map_printed(&self, mut printed: Printed) -> Printed { + self.map_markers(&mut printed.sourcemap); + self.map_verbatim_ranges(&mut printed.verbatim_ranges); + + printed + } + + /// Maps the printers source map marker to the source positions. + fn map_markers(&self, markers: &mut [SourceMarker]) { + if self.deleted_ranges.is_empty() { + return; + } + + let mut previous_marker: Option = None; + let mut next_range_index = 0; + + for marker in markers { + // It's not guaranteed that markers are sorted by source location (line suffix comments). + // It can, therefore, be necessary to navigate backwards again. + // In this case, do a binary search for the index of the next deleted range (`O(log(n)`). + let out_of_order_marker = + previous_marker.map_or(false, |previous| previous.source > marker.source); + + if out_of_order_marker { + let index = self + .deleted_ranges + .binary_search_by_key(&marker.source, |range| range.transformed_start()); + + match index { + // Direct match + Ok(index) => { + next_range_index = index + 1; + } + Err(index) => next_range_index = index, + } + } else { + // Find the range for this mapping. In most cases this is a no-op or only involves a single step + // because markers are most of the time in increasing source order. + while next_range_index < self.deleted_ranges.len() { + let next_range = &self.deleted_ranges[next_range_index]; + + if next_range.transformed_start() > marker.source { + break; + } + + next_range_index += 1; + } + } + + previous_marker = Some(*marker); + + let current_range = if next_range_index == 0 { + None + } else { + self.deleted_ranges.get(next_range_index - 1) + }; + + let source = + self.source_offset_with_range(marker.source, RangePosition::Start, current_range); + + marker.source = source; + } + } + + fn map_verbatim_ranges(&self, ranges: &mut [TextRange]) { + for range in ranges { + *range = self.source_range(*range) + } + } +} + +#[derive(Debug, Copy, Clone)] +struct TrimmedNodeRangeMapping { + /// The original trimmed range of the node. + /// + /// ```javascript + /// (a + b) + /// ``` + /// + /// `1..6` `a + b` + original_range: TextRange, + + /// The range to which the trimmed range of the node should be extended + /// ```javascript + /// (a + b) + /// ``` + /// + /// `0..7` for `a + b` if its range should also include the parenthesized range. + extended_range: TextRange, +} + +#[derive(Copy, Clone, Debug)] +enum RangePosition { + Start, + End, +} + +/// Stores the information about a range in the source document that isn't present in the transformed document +/// and provides means to map the transformed position back to the source position. +/// +/// # Examples +/// +/// ```javascript +/// (a + b) +/// ``` +/// +/// A transform that removes the parentheses from the above expression removes the ranges `0..1` (`(` token) +/// and `6..7` (`)` token) and the source map creates one [DeletedRange] for each: +/// +/// ```text +/// DeletedRange { +/// source_range: 0..1, +/// total_length_preceding_deleted_ranges: 0, +/// }, +/// DeletedRange { +/// source_range: 6..7, +/// total_length_preceding_deleted_ranges: 1, +/// } +/// ``` +/// +/// The first range indicates that the range `0..1` for the `(` token has been removed. The second range +/// indicates that the range `6..7` for the `)` token has been removed and it stores that, up to this point, +/// but not including, 1 more byte has been removed. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +struct DeletedRange { + /// The range in the source document of the bytes that have been omitted from the transformed document. + source_range: TextRange, + + /// The accumulated count of all removed bytes up to (but not including) the start of this range. + total_length_preceding_deleted_ranges: TextSize, +} + +impl DeletedRange { + fn new(source_range: TextRange, total_length_preceding_deleted_ranges: TextSize) -> Self { + debug_assert!(source_range.start() >= total_length_preceding_deleted_ranges, "The total number of deleted bytes ({:?}) can not exceed the offset from the start in the source document ({:?}). This is a bug in the source map implementation.", total_length_preceding_deleted_ranges, source_range.start()); + + Self { + source_range, + total_length_preceding_deleted_ranges, + } + } + + /// The number of deleted characters starting from [source offset](DeletedRange::source_start). + fn len(&self) -> TextSize { + self.source_range.len() + } + + /// The start position in bytes in the source document of the omitted sequence in the transformed document. + fn source_start(&self) -> TextSize { + self.source_range.start() + } + + /// The end position in bytes in the source document of the omitted sequence in the transformed document. + fn source_end(&self) -> TextSize { + self.source_range.end() + } + + /// Returns the byte position of [DeleteRange::source_start] in the transformed document. + fn transformed_start(&self) -> TextSize { + self.source_range.start() - self.total_length_preceding_deleted_ranges + } +} + +/// Builder for creating a source map. +#[derive(Debug)] +pub struct TransformSourceMapBuilder { + /// The original source text of the tree before it was transformed. + source_text: SyntaxNodeText, + + /// The mappings in increasing order by transformed offset. + deleted_ranges: Vec, + + /// The keys are a position in the source map where a trimmed node starts or ends. + /// The values are the metadata about a trimmed node range + mapped_node_ranges: FxHashMap, +} + +impl TransformSourceMapBuilder { + /// Creates a new builder for a source map that maps positions back to the passed `root` tree. + pub fn new(root: &SyntaxNode) -> Self { + Self { + source_text: root.text(), + deleted_ranges: Vec::new(), + mapped_node_ranges: FxHashMap::default(), + } + } + + /// Adds a new mapping for a deleted character range. + pub fn add_deleted_range(&mut self, source_range: TextRange) { + self.deleted_ranges.push(source_range); + } + + /// Adds a mapping to widen a nodes trimmed range. + /// + /// The formatter uses the trimmed range when formatting a node in verbatim either because the node + /// failed to format because of a syntax error or because it's formatting is suppressed with a `rome-ignore format:` comment. + /// + /// This method adds a mapping to widen a nodes trimmed range to enclose another range instead. This is + /// e.g. useful when removing parentheses around expressions where `(/* comment */ a /* comment */)` because + /// the trimmed range of `a` should now enclose the full range including the `(` and `)` tokens to ensure + /// that the parentheses are retained when printing that node in verbatim style. + pub fn extend_trimmed_node_range( + &mut self, + original_range: TextRange, + extended_range: TextRange, + ) { + let mapping = TrimmedNodeRangeMapping { + original_range, + extended_range, + }; + + self.mapped_node_ranges + .insert(original_range.start(), mapping); + self.mapped_node_ranges + .insert(original_range.end(), mapping); + } + + /// Creates a source map that performs single position lookups in `O(log(n))`. + pub fn finish(mut self) -> TransformSourceMap { + let mut merged_mappings = Vec::with_capacity(self.deleted_ranges.len()); + + if !self.deleted_ranges.is_empty() { + self.deleted_ranges + .sort_by(|a, b| match a.start().cmp(&b.start()) { + Ordering::Equal => a.end().cmp(&b.end()), + ordering => ordering, + }); + + let mut last_mapping = DeletedRange::new( + // SAFETY: Safe because of the not empty check above + self.deleted_ranges[0], + TextSize::default(), + ); + + let mut transformed_offset = last_mapping.len(); + + for range in self.deleted_ranges.drain(1..) { + // Merge adjacent ranges to ensure there's only ever a single mapping starting at the same transformed offset. + if last_mapping.source_range.end() == range.start() { + last_mapping.source_range = last_mapping.source_range.cover(range); + } else { + merged_mappings.push(last_mapping); + + last_mapping = DeletedRange::new(range, transformed_offset); + } + transformed_offset += range.len(); + } + + merged_mappings.push(last_mapping); + } + + TransformSourceMap { + source_text: self.source_text, + deleted_ranges: merged_mappings, + mapped_node_ranges: self.mapped_node_ranges, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{TextRange, TextSize, TransformSourceMapBuilder}; + use rome_rowan::raw_language::{RawLanguageKind, RawSyntaxTreeBuilder}; + + #[test] + fn range_mapping() { + let mut cst_builder = RawSyntaxTreeBuilder::new(); + cst_builder.start_node(RawLanguageKind::ROOT); + // The shape of the tree doesn't matter for the test case + cst_builder.token(RawLanguageKind::STRING_TOKEN, "(a + (((b + c)) + d)) + e"); + cst_builder.finish_node(); + let root = cst_builder.finish(); + + let mut builder = TransformSourceMapBuilder::new(&root); + + // Add mappings for all removed parentheses. + + // `(` + builder.add_deleted_range(TextRange::new(TextSize::from(0), TextSize::from(1))); + + // `(((` + builder.add_deleted_range(TextRange::new(TextSize::from(5), TextSize::from(6))); + // Ranges can be added out of order + builder.add_deleted_range(TextRange::new(TextSize::from(7), TextSize::from(8))); + builder.add_deleted_range(TextRange::new(TextSize::from(6), TextSize::from(7))); + + // `))` + builder.add_deleted_range(TextRange::new(TextSize::from(13), TextSize::from(14))); + builder.add_deleted_range(TextRange::new(TextSize::from(14), TextSize::from(15))); + + // `))` + builder.add_deleted_range(TextRange::new(TextSize::from(19), TextSize::from(20))); + builder.add_deleted_range(TextRange::new(TextSize::from(20), TextSize::from(21))); + + let source_map = builder.finish(); + + // The following mapping assume the tranformed string to be (including whitespace): + // "a + b + c + d + e"; + + // `a` + assert_eq!( + source_map.source_range(TextRange::new(TextSize::from(0), TextSize::from(1))), + TextRange::new(TextSize::from(1), TextSize::from(2)) + ); + + // `b` + assert_eq!( + source_map.source_range(TextRange::new(TextSize::from(4), TextSize::from(5))), + TextRange::new(TextSize::from(8), TextSize::from(9)) + ); + + // `c` + assert_eq!( + source_map.source_range(TextRange::new(TextSize::from(8), TextSize::from(9))), + TextRange::new(TextSize::from(12), TextSize::from(13)) + ); + + // `d` + assert_eq!( + source_map.source_range(TextRange::new(TextSize::from(12), TextSize::from(13))), + TextRange::new(TextSize::from(18), TextSize::from(19)) + ); + + // `e` + assert_eq!( + source_map.source_range(TextRange::new(TextSize::from(16), TextSize::from(17))), + TextRange::new(TextSize::from(24), TextSize::from(25)) + ); + } + + #[test] + fn trimmed_range() { + // Build up a tree for `((a))` + // Don't mind the unknown nodes, it doesn't really matter what the nodes are. + let mut cst_builder = RawSyntaxTreeBuilder::new(); + cst_builder.start_node(RawLanguageKind::ROOT); + + cst_builder.start_node(RawLanguageKind::UNKNOWN); + cst_builder.token(RawLanguageKind::STRING_TOKEN, "("); + + cst_builder.start_node(RawLanguageKind::UNKNOWN); + cst_builder.token(RawLanguageKind::UNKNOWN, "("); + + cst_builder.start_node(RawLanguageKind::LITERAL_EXPRESSION); + cst_builder.token(RawLanguageKind::STRING_TOKEN, "a"); + cst_builder.finish_node(); + + cst_builder.token(RawLanguageKind::UNKNOWN, ")"); + cst_builder.finish_node(); + + cst_builder.token(RawLanguageKind::UNKNOWN, ")"); + cst_builder.finish_node(); + + cst_builder.token(RawLanguageKind::UNKNOWN, ";"); + + cst_builder.finish_node(); + + let root = cst_builder.finish(); + + assert_eq!(&root.text(), "((a));"); + + let mut unknowns = root + .descendants() + .filter(|node| node.kind() == RawLanguageKind::UNKNOWN); + + // `((a))` + let outer = unknowns.next().unwrap(); + + // `(a)` + let inner = unknowns.next().unwrap(); + + // `a` + let expression = root + .descendants() + .find(|node| node.kind() == RawLanguageKind::LITERAL_EXPRESSION) + .unwrap(); + + let mut builder = TransformSourceMapBuilder::new(&root); + + // Add mappings for all removed parentheses. + builder.add_deleted_range(TextRange::new(TextSize::from(0), TextSize::from(2))); + builder.add_deleted_range(TextRange::new(TextSize::from(3), TextSize::from(5))); + + // Extend `a` to the range of `(a)` + builder + .extend_trimmed_node_range(expression.text_trimmed_range(), inner.text_trimmed_range()); + // Extend `(a)` to the range of `((a))` + builder.extend_trimmed_node_range(inner.text_trimmed_range(), outer.text_trimmed_range()); + + let source_map = builder.finish(); + + // Query `a` + assert_eq!( + source_map.trimmed_source_text_from_transformed_range(TextRange::new( + TextSize::from(0), + TextSize::from(1) + )), + "((a))" + ); + + // Query `a;` expression + assert_eq!( + source_map.trimmed_source_text_from_transformed_range(TextRange::new( + TextSize::from(0), + TextSize::from(2) + )), + "((a));" + ); + } +} diff --git a/crates/rome_js_formatter/Cargo.toml b/crates/rome_js_formatter/Cargo.toml index 9a04cf2d11b..cf174a7ac47 100644 --- a/crates/rome_js_formatter/Cargo.toml +++ b/crates/rome_js_formatter/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT" [dependencies] cfg-if = "1.0.0" rome_js_syntax = { path = "../rome_js_syntax" } +rome_js_factory = { path = "../rome_js_factory" } rome_formatter = { path = "../rome_formatter" } rome_rowan = { path = "../rome_rowan" } tracing = { version = "0.1.31", default-features = false, features = ["std"] } diff --git a/crates/rome_js_formatter/src/builders.rs b/crates/rome_js_formatter/src/builders.rs index 3651f4147f0..01403c64e60 100644 --- a/crates/rome_js_formatter/src/builders.rs +++ b/crates/rome_js_formatter/src/builders.rs @@ -2,10 +2,11 @@ use crate::prelude::*; use crate::AsFormat; use rome_formatter::token::{FormatInserted, FormatInsertedCloseParen, FormatInsertedOpenParen}; use rome_formatter::{ - format_args, write, Argument, Arguments, CstFormatContext, GroupId, PreambleBuffer, VecBuffer, + format_args, write, Argument, Arguments, CstFormatContext, FormatContext, GroupId, + PreambleBuffer, VecBuffer, }; use rome_js_syntax::{JsLanguage, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken}; -use rome_rowan::{AstNode, Direction, Language, SyntaxElement, SyntaxTriviaPiece}; +use rome_rowan::{AstNode, Direction, Language, SyntaxElement, SyntaxTriviaPiece, TextRange}; /// Formats a node using its [`AsFormat`] implementation but falls back to printing the node as /// it is in the source document if the formatting returns an [`FormatError`]. @@ -207,11 +208,22 @@ impl Format for FormatVerbatimNode<'_> { .fmt(f) } + let trimmed_source_range = f.context().source_map().map_or_else( + || self.node.text_trimmed_range(), + |source_map| source_map.trimmed_source_range(self.node), + ); + let mut buffer = VecBuffer::new(f.state_mut()); write!( buffer, - [format_with(|f| { + [format_with(|f: &mut JsFormatter| { + fn source_range(f: &JsFormatter, range: TextRange) -> TextRange { + f.context() + .source_map() + .map_or_else(|| range, |source_map| source_map.source_range(range)) + } + for leading_trivia in self .node .first_leading_trivia() @@ -219,26 +231,53 @@ impl Format for FormatVerbatimNode<'_> { .flat_map(|trivia| trivia.pieces()) .skip_while(skip_whitespace) { + let trivia_source_range = source_range(f, leading_trivia.text_range()); + + if trivia_source_range.start() >= trimmed_source_range.start() { + break; + } + write_trivia_token(f, leading_trivia)?; } + let original_source = f + .context() + .source_map() + .map_or_else( + || self.node.text_trimmed(), + |source_map| source_map.text().slice(trimmed_source_range), + ) + .to_string(); + dynamic_text( - &normalize_newlines(&self.node.text_trimmed().to_string(), LINE_TERMINATORS), + &normalize_newlines(&original_source, LINE_TERMINATORS), self.node.text_trimmed_range().start(), ) .fmt(f)?; - // Clippy false positive: SkipWhile does not implement DoubleEndedIterator - #[allow(clippy::needless_collect)] - let trailing_trivia: Vec<_> = self + let mut trailing_trivia = self .node .last_trailing_trivia() .into_iter() - .flat_map(|trivia| trivia.pieces().rev()) - .skip_while(skip_whitespace) - .collect(); + .flat_map(|trivia| trivia.pieces()); + + let mut trailing_back = trailing_trivia.by_ref().rev().peekable(); + + while let Some(trailing) = trailing_back.peek() { + let is_whitespace = skip_whitespace(trailing); + + let trailing_source_range = source_range(f, trailing.text_range()); + let is_in_trimmed_range = + trailing_source_range.start() < trimmed_source_range.end(); + + if is_whitespace || is_in_trimmed_range { + trailing_back.next(); + } else { + break; + } + } - for trailing_trivia in trailing_trivia.into_iter().rev() { + for trailing_trivia in trailing_trivia { write_trivia_token(f, trailing_trivia)?; } diff --git a/crates/rome_js_formatter/src/context.rs b/crates/rome_js_formatter/src/context.rs index b6816fe5471..5b2234d0bd6 100644 --- a/crates/rome_js_formatter/src/context.rs +++ b/crates/rome_js_formatter/src/context.rs @@ -1,7 +1,7 @@ use rome_formatter::printer::PrinterOptions; use rome_formatter::{ CommentKind, CommentStyle, Comments, CstFormatContext, FormatContext, FormatOptions, - IndentStyle, LineWidth, + IndentStyle, LineWidth, TransformSourceMap, }; use rome_js_syntax::suppression::{parse_suppression_comment, SuppressionCategory}; use rome_js_syntax::{JsLanguage, JsSyntaxKind, SourceType}; @@ -17,6 +17,8 @@ pub struct JsFormatContext { /// The comments of the nodes and tokens in the program. comments: Rc>, + + source_map: Option, } impl JsFormatContext { @@ -24,8 +26,14 @@ impl JsFormatContext { Self { options, comments: Rc::new(comments), + ..JsFormatContext::default() } } + + pub fn with_source_map(mut self, source_map: Option) -> Self { + self.source_map = source_map; + self + } } #[derive(Eq, PartialEq, Debug, Copy, Clone, Hash)] @@ -49,6 +57,10 @@ impl FormatContext for JsFormatContext { fn options(&self) -> &Self::Options { &self.options } + + fn source_map(&self) -> Option<&TransformSourceMap> { + self.source_map.as_ref() + } } impl CstFormatContext for JsFormatContext { @@ -115,10 +127,6 @@ impl JsFormatOptions { self } - pub fn line_width(&self) -> LineWidth { - self.line_width - } - pub fn quote_style(&self) -> QuoteStyle { self.quote_style } diff --git a/crates/rome_js_formatter/src/js/assignments/array_assignment_pattern.rs b/crates/rome_js_formatter/src/js/assignments/array_assignment_pattern.rs index 3ae9ee52af6..a27dcda70ab 100644 --- a/crates/rome_js_formatter/src/js/assignments/array_assignment_pattern.rs +++ b/crates/rome_js_formatter/src/js/assignments/array_assignment_pattern.rs @@ -1,7 +1,7 @@ -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use crate::prelude::*; use rome_formatter::write; -use rome_js_syntax::{JsAnyAssignmentPattern, JsArrayAssignmentPattern}; +use rome_js_syntax::JsArrayAssignmentPattern; use rome_js_syntax::{JsArrayAssignmentPatternFields, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -29,18 +29,6 @@ impl FormatNodeRule for FormatJsArrayAssignmentPattern } } -impl AssignmentNode for JsArrayAssignmentPattern { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - self.into() - } -} - impl NeedsParentheses for JsArrayAssignmentPattern { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/assignments/computed_member_assignment.rs b/crates/rome_js_formatter/src/js/assignments/computed_member_assignment.rs index 47d3fd189a1..b008aeae6b0 100644 --- a/crates/rome_js_formatter/src/js/assignments/computed_member_assignment.rs +++ b/crates/rome_js_formatter/src/js/assignments/computed_member_assignment.rs @@ -1,10 +1,8 @@ use crate::prelude::*; use crate::js::expressions::computed_member_expression::JsAnyComputedMemberLike; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; -use rome_js_syntax::{ - JsAnyAssignment, JsAnyAssignmentPattern, JsComputedMemberAssignment, JsSyntaxNode, -}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsComputedMemberAssignment, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsComputedMemberAssignment; @@ -23,18 +21,6 @@ impl FormatNodeRule for FormatJsComputedMemberAssign } } -impl AssignmentNode for JsComputedMemberAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for JsComputedMemberAssignment { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/assignments/identifier_assignment.rs b/crates/rome_js_formatter/src/js/assignments/identifier_assignment.rs index 3573cf5c46c..08524331d1b 100644 --- a/crates/rome_js_formatter/src/js/assignments/identifier_assignment.rs +++ b/crates/rome_js_formatter/src/js/assignments/identifier_assignment.rs @@ -1,9 +1,9 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyAssignment, JsForOfStatement, JsIdentifierAssignmentFields}; -use rome_js_syntax::{JsAnyAssignmentPattern, JsIdentifierAssignment, JsSyntaxNode}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsForOfStatement, JsIdentifierAssignmentFields}; +use rome_js_syntax::{JsIdentifierAssignment, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsIdentifierAssignment; @@ -20,18 +20,6 @@ impl FormatNodeRule for FormatJsIdentifierAssignment { } } -impl AssignmentNode for JsIdentifierAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for JsIdentifierAssignment { #[inline] fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { diff --git a/crates/rome_js_formatter/src/js/assignments/object_assignment_pattern.rs b/crates/rome_js_formatter/src/js/assignments/object_assignment_pattern.rs index 831621c63ef..f9b39bafa77 100644 --- a/crates/rome_js_formatter/src/js/assignments/object_assignment_pattern.rs +++ b/crates/rome_js_formatter/src/js/assignments/object_assignment_pattern.rs @@ -1,8 +1,8 @@ -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use crate::prelude::*; use crate::utils::JsObjectPatternLike; use rome_formatter::write; -use rome_js_syntax::{JsAnyAssignmentPattern, JsObjectAssignmentPattern, JsSyntaxNode}; +use rome_js_syntax::{JsObjectAssignmentPattern, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsObjectAssignmentPattern; @@ -21,18 +21,6 @@ impl FormatNodeRule for FormatJsObjectAssignmentPatte } } -impl AssignmentNode for JsObjectAssignmentPattern { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - self.into() - } -} - impl NeedsParentheses for JsObjectAssignmentPattern { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/assignments/parenthesized_assignment.rs b/crates/rome_js_formatter/src/js/assignments/parenthesized_assignment.rs index 658e61a36f3..7a29bd4c2f0 100644 --- a/crates/rome_js_formatter/src/js/assignments/parenthesized_assignment.rs +++ b/crates/rome_js_formatter/src/js/assignments/parenthesized_assignment.rs @@ -1,7 +1,7 @@ -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use crate::prelude::*; use rome_formatter::write; -use rome_js_syntax::{JsAnyAssignmentPattern, JsParenthesizedAssignment}; +use rome_js_syntax::JsParenthesizedAssignment; use rome_js_syntax::{JsParenthesizedAssignmentFields, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -22,9 +22,9 @@ impl FormatNodeRule for FormatJsParenthesizedAssignme write![ f, [ - format_removed(&l_paren_token?), + l_paren_token.format(), assignment.format(), - format_removed(&r_paren_token?), + r_paren_token.format(), ] ] } @@ -34,22 +34,6 @@ impl FormatNodeRule for FormatJsParenthesizedAssignme } } -impl AssignmentNode for JsParenthesizedAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - let assignment = self.assignment().unwrap_or_else(|_| self.clone().into()); - - JsAnyAssignmentPattern::JsAnyAssignment(assignment) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - let assignment = self.assignment().unwrap_or_else(|_| self.into()); - - JsAnyAssignmentPattern::JsAnyAssignment(assignment) - } -} - impl NeedsParentheses for JsParenthesizedAssignment { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/assignments/static_member_assignment.rs b/crates/rome_js_formatter/src/js/assignments/static_member_assignment.rs index c55bdab1201..899744853c5 100644 --- a/crates/rome_js_formatter/src/js/assignments/static_member_assignment.rs +++ b/crates/rome_js_formatter/src/js/assignments/static_member_assignment.rs @@ -1,9 +1,7 @@ use crate::js::expressions::static_member_expression::JsAnyStaticMemberLike; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use crate::prelude::*; -use rome_js_syntax::{ - JsAnyAssignment, JsAnyAssignmentPattern, JsStaticMemberAssignment, JsSyntaxNode, -}; +use rome_js_syntax::{JsStaticMemberAssignment, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsStaticMemberAssignment; @@ -18,18 +16,6 @@ impl FormatNodeRule for FormatJsStaticMemberAssignment } } -impl AssignmentNode for JsStaticMemberAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for JsStaticMemberAssignment { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/auxiliary/new_target.rs b/crates/rome_js_formatter/src/js/auxiliary/new_target.rs index 4305a61bf61..7fb772fbf97 100644 --- a/crates/rome_js_formatter/src/js/auxiliary/new_target.rs +++ b/crates/rome_js_formatter/src/js/auxiliary/new_target.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, NewTargetFields}; +use rome_js_syntax::NewTargetFields; use rome_js_syntax::{JsSyntaxNode, NewTarget}; #[derive(Debug, Clone, Default)] @@ -40,14 +40,3 @@ impl NeedsParentheses for NewTarget { false } } - -impl ExpressionNode for NewTarget { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/classes/extends_clause.rs b/crates/rome_js_formatter/src/js/classes/extends_clause.rs index 8d37c889ae6..64b00a294a9 100644 --- a/crates/rome_js_formatter/src/js/classes/extends_clause.rs +++ b/crates/rome_js_formatter/src/js/classes/extends_clause.rs @@ -1,6 +1,5 @@ use crate::prelude::*; -use crate::parentheses::resolve_parent; use rome_formatter::{format_args, write}; use rome_js_syntax::JsExtendsClauseFields; use rome_js_syntax::JsSyntaxKind::JS_ASSIGNMENT_EXPRESSION; @@ -31,8 +30,7 @@ impl FormatNodeRule for FormatJsExtendsClause { if node .syntax() - .parent() - .and_then(|node| resolve_parent(&node)) + .grand_parent() .map_or(false, |p| p.kind() == JS_ASSIGNMENT_EXPRESSION) { if super_class.syntax().has_leading_comments() || has_trailing_comments { 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 880c310bcc3..f7dd795b835 100644 --- a/crates/rome_js_formatter/src/js/expressions/array_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/array_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsArrayExpressionFields}; +use rome_js_syntax::JsArrayExpressionFields; use rome_js_syntax::{JsArrayExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -44,15 +44,3 @@ impl NeedsParentheses for JsArrayExpression { false } } - -impl ExpressionNode for JsArrayExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} 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 d4a85e26b72..7fc6fbc101d 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 @@ -3,7 +3,7 @@ use rome_formatter::{format_args, write}; use crate::parentheses::{ is_binary_like_left_or_right, is_conditional_test, - update_or_lower_expression_needs_parentheses, ExpressionNode, NeedsParentheses, + update_or_lower_expression_needs_parentheses, NeedsParentheses, }; use crate::utils::{resolve_left_most_expression, JsAnyBinaryLikeLeftExpression}; use rome_js_syntax::{ @@ -86,13 +86,12 @@ impl FormatNodeRule for FormatJsArrowFunctionExpressi // going to get broken anyways. let body_has_soft_line_break = match &body { JsFunctionBody(_) => true, - JsAnyExpression(expr) => match expr.resolve() { + JsAnyExpression(expr) => match expr { JsArrowFunctionExpression(_) | JsArrayExpression(_) | JsObjectExpression(_) - | JsParenthesizedExpression(_) | JsxTagExpression(_) => true, - JsTemplate(template) => is_multiline_template_starting_on_same_line(&template), + JsTemplate(template) => is_multiline_template_starting_on_same_line(template), JsSequenceExpression(_) => { return write!( f, @@ -116,9 +115,7 @@ impl FormatNodeRule for FormatJsArrowFunctionExpressi // case and added by the object expression itself let should_add_parens = match &body { JsAnyExpression(expression) => { - let resolved = expression.resolve(); - - let is_conditional = matches!(resolved, JsConditionalExpression(_)); + let is_conditional = matches!(expression, JsConditionalExpression(_)); let are_parentheses_mandatory = matches!( resolve_left_most_expression(expression), JsAnyBinaryLikeLeftExpression::JsAnyExpression( @@ -178,18 +175,6 @@ impl NeedsParentheses for JsArrowFunctionExpression { } } -impl ExpressionNode for JsArrowFunctionExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs b/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs index 6fbceadd377..d93497e65f6 100644 --- a/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/assignment_expression.rs @@ -2,15 +2,14 @@ use crate::prelude::*; use crate::utils::JsAnyAssignmentLike; use crate::parentheses::{ - is_arrow_function_body, is_first_in_statement, ExpressionNode, FirstInStatementMode, - NeedsParentheses, + is_arrow_function_body, is_first_in_statement, FirstInStatementMode, NeedsParentheses, }; use rome_formatter::write; use rome_js_syntax::{ - JsAnyAssignmentPattern, JsAnyExpression, JsAnyForInitializer, JsArrowFunctionExpression, - JsAssignmentExpression, JsComputedMemberName, JsExpressionStatement, JsForStatement, - JsSequenceExpression, JsSyntaxKind, JsSyntaxNode, + JsAnyAssignmentPattern, JsAnyForInitializer, JsArrowFunctionExpression, JsAssignmentExpression, + JsComputedMemberName, JsExpressionStatement, JsForStatement, JsSequenceExpression, + JsSyntaxKind, JsSyntaxNode, }; use rome_rowan::{match_ast, AstNode}; @@ -42,14 +41,14 @@ impl NeedsParentheses for JsAssignmentExpression { JsForStatement(for_statement) => { let is_initializer = match for_statement.initializer() { Some(JsAnyForInitializer::JsAnyExpression(expression)) => { - &expression.resolve_syntax() == self.syntax() + expression.syntax() == self.syntax() } None | Some(_) => false, }; let is_update = for_statement .update() - .map(ExpressionNode::into_resolved_syntax) + .map(AstNode::into_syntax) .as_ref() == Some(self.syntax()); @@ -102,18 +101,6 @@ impl NeedsParentheses for JsAssignmentExpression { } } -impl ExpressionNode for JsAssignmentExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/await_expression.rs b/crates/rome_js_formatter/src/js/expressions/await_expression.rs index 1a4d24ae050..466405e2534 100644 --- a/crates/rome_js_formatter/src/js/expressions/await_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/await_expression.rs @@ -3,10 +3,10 @@ use rome_formatter::write; use crate::parentheses::{ is_binary_like_left_or_right, is_callee, is_conditional_test, is_member_object, is_spread, - update_or_lower_expression_needs_parentheses, ExpressionNode, NeedsParentheses, + update_or_lower_expression_needs_parentheses, NeedsParentheses, }; -use rome_js_syntax::{JsAnyExpression, JsAwaitExpression, JsSyntaxNode}; +use rome_js_syntax::{JsAwaitExpression, JsSyntaxNode}; use rome_js_syntax::{JsAwaitExpressionFields, JsSyntaxKind}; #[derive(Debug, Clone, Default)] @@ -22,7 +22,7 @@ impl FormatNodeRule for FormatJsAwaitExpression { let format_inner = format_with(|f| write![f, [await_token.format(), space(), argument.format()]]); - let parent = node.resolve_parent(); + let parent = node.syntax().parent(); if let Some(parent) = parent { if is_callee(node.syntax(), &parent) || is_member_object(node.syntax(), &parent) { @@ -81,18 +81,6 @@ pub(super) fn await_or_yield_needs_parens(parent: &JsSyntaxNode, node: &JsSyntax } } -impl ExpressionNode for JsAwaitExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/big_int_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/big_int_literal_expression.rs index cc4a50c9a9d..21b03f8d48a 100644 --- a/crates/rome_js_formatter/src/js/expressions/big_int_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/big_int_literal_expression.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use crate::prelude::*; use crate::utils::string_utils::ToAsciiLowercaseCow; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsBigIntLiteralExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::JsBigIntLiteralExpressionFields; use rome_js_syntax::{JsBigIntLiteralExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -40,18 +40,6 @@ impl FormatNodeRule for FormatJsBigIntLiteralExpressi } } -impl ExpressionNode for JsBigIntLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} - impl NeedsParentheses for JsBigIntLiteralExpression { #[inline(always)] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/js/expressions/binary_expression.rs b/crates/rome_js_formatter/src/js/expressions/binary_expression.rs index 3a8d952d2b1..777ecdd3a38 100644 --- a/crates/rome_js_formatter/src/js/expressions/binary_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/binary_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use crate::utils::{needs_binary_like_parentheses, JsAnyBinaryLikeExpression}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsBinaryExpression, JsSyntaxNode}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsBinaryExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsBinaryExpression; @@ -27,18 +27,6 @@ impl NeedsParentheses for JsBinaryExpression { } } -impl ExpressionNode for JsBinaryExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { use crate::{assert_needs_parentheses, assert_not_needs_parentheses}; diff --git a/crates/rome_js_formatter/src/js/expressions/boolean_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/boolean_literal_expression.rs index 883f1869170..2bb0bb9e151 100644 --- a/crates/rome_js_formatter/src/js/expressions/boolean_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/boolean_literal_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsBooleanLiteralExpressionFields}; +use rome_js_syntax::JsBooleanLiteralExpressionFields; use rome_js_syntax::{JsBooleanLiteralExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -34,15 +34,3 @@ impl NeedsParentheses for JsBooleanLiteralExpression { false } } - -impl ExpressionNode for JsBooleanLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} 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 437c28adec1..8958e7f90bd 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs @@ -1,5 +1,4 @@ use crate::builders::{format_close_delimiter, format_open_delimiter}; -use crate::parentheses::ExpressionNode; use crate::prelude::*; use crate::utils::{is_call_like_expression, write_arguments_multi_line}; use rome_formatter::{format_args, write}; @@ -252,7 +251,7 @@ fn should_group_first_argument(list: &JsCallArgumentList) -> SyntaxResult let has_comments = first.syntax().has_comments_direct(); - let is_function_like = match resolve_call_argument_expression(&first) { + let is_function_like = match first.as_js_any_expression() { Some(JsAnyExpression::JsFunctionExpression(_)) => true, Some(JsAnyExpression::JsArrowFunctionExpression(arrow)) => { matches!(arrow.body()?, JsAnyFunctionBody::JsFunctionBody(_)) @@ -260,7 +259,7 @@ fn should_group_first_argument(list: &JsCallArgumentList) -> SyntaxResult _ => false, }; - let (second_arg_is_function_like, can_group) = match resolve_call_argument_expression(&second) { + let (second_arg_is_function_like, can_group) = match second.as_js_any_expression() { Some(second_expression) => { let second_arg_is_function_like = matches!( &second_expression, @@ -270,7 +269,7 @@ fn should_group_first_argument(list: &JsCallArgumentList) -> SyntaxResult ); ( second_arg_is_function_like, - could_group_expression_argument(&second_expression, false)?, + could_group_expression_argument(second_expression, false)?, ) } None => (false, false), @@ -322,7 +321,7 @@ fn could_group_expression_argument( argument: &JsAnyExpression, is_arrow_recursion: bool, ) -> SyntaxResult { - let result = match argument.resolve() { + let result = match argument { JsAnyExpression::JsObjectExpression(object_expression) => { object_expression.members().len() > 0 || object_expression @@ -375,7 +374,7 @@ fn could_group_expression_argument( let expression_body = match &body { JsAnyFunctionBody::JsFunctionBody(_) => None, - JsAnyFunctionBody::JsAnyExpression(expression) => Some(expression.resolve()), + JsAnyFunctionBody::JsAnyExpression(expression) => Some(expression), }; let body_is_delimited = matches!(body, JsAnyFunctionBody::JsFunctionBody(_)) @@ -406,7 +405,7 @@ fn could_group_expression_argument( && is_nested_arrow_function && can_group_type && (!is_arrow_recursion - && (is_call_like_expression(&any_expression) + && (is_call_like_expression(any_expression) || matches!( any_expression, JsAnyExpression::JsConditionalExpression(_) @@ -433,7 +432,7 @@ fn is_react_hook_with_deps_array( second_argument: &JsAnyCallArgument, ) -> SyntaxResult { let first_expression = match first_argument { - JsAnyCallArgument::JsAnyExpression(expression) => Some(expression.resolve()), + JsAnyCallArgument::JsAnyExpression(expression) => Some(expression), _ => None, }; @@ -520,13 +519,13 @@ fn is_framework_test_call(payload: IsTestFrameworkCallPayload) -> SyntaxResult resolve_call_argument_expression(argument), + Ok(argument) => argument.as_js_any_expression(), _ => None, }); @@ -578,15 +577,6 @@ fn is_framework_test_call(payload: IsTestFrameworkCallPayload) -> SyntaxResult Option { - match argument { - JsAnyCallArgument::JsAnyExpression(expression) => Some(expression.resolve()), - _ => None, - } -} - /// This function checks if a call expressions has one of the following members: /// - `it` /// - `it.only` @@ -693,10 +683,6 @@ fn matches_test_call(callee: &JsAnyExpression) -> SyntaxResult break, } } - JsAnyExpression::JsParenthesizedExpression(parenthesized) => { - i -= 1; // Don't increment the depth - parenthesized.expression()? - } _ => break, }; } @@ -767,15 +753,6 @@ mod test { ); } - #[test] - fn matches_parentheses() { - let call_expression = extract_call_expression("(test.describe.parallel).only();"); - assert_eq!( - contains_a_test_pattern(&call_expression.callee().unwrap()), - Ok(true) - ); - } - #[test] fn doesnt_static_member_expression_deep() { let call_expression = extract_call_expression("test.describe.parallel.only.AHAHA();"); 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 7566246f39d..3d1d3560d68 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use crate::utils::get_member_chain; -use rome_js_syntax::{JsAnyExpression, JsCallExpression, JsSyntaxKind, JsSyntaxNode}; +use rome_js_syntax::{JsCallExpression, JsSyntaxKind, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsCallExpression; @@ -25,18 +25,6 @@ impl NeedsParentheses for JsCallExpression { } } -impl ExpressionNode for JsCallExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/class_expression.rs b/crates/rome_js_formatter/src/js/expressions/class_expression.rs index 167b372b120..45951b11812 100644 --- a/crates/rome_js_formatter/src/js/expressions/class_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/class_expression.rs @@ -2,9 +2,9 @@ use crate::prelude::*; use crate::utils::format_class::FormatClass; use crate::parentheses::{ - is_callee, is_first_in_statement, ExpressionNode, FirstInStatementMode, NeedsParentheses, + is_callee, is_first_in_statement, FirstInStatementMode, NeedsParentheses, }; -use rome_js_syntax::{JsAnyExpression, JsClassExpression, JsSyntaxNode}; +use rome_js_syntax::{JsClassExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsClassExpression; @@ -29,18 +29,6 @@ impl NeedsParentheses for JsClassExpression { } } -impl ExpressionNode for JsClassExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { 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 c9a84b5eb16..649f6ee37ba 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 @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::{format_args, write}; use rome_js_syntax::{ JsAnyExpression, JsAnyLiteralExpression, JsComputedMemberAssignment, @@ -121,18 +121,6 @@ impl NeedsParentheses for JsComputedMemberExpression { } } -impl ExpressionNode for JsComputedMemberExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs b/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs index aecf825431c..9dfe44eae5e 100644 --- a/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs @@ -3,9 +3,9 @@ use crate::utils::JsAnyConditional; use crate::parentheses::{ is_binary_like_left_or_right, is_conditional_test, is_spread, - update_or_lower_expression_needs_parentheses, ExpressionNode, NeedsParentheses, + update_or_lower_expression_needs_parentheses, NeedsParentheses, }; -use rome_js_syntax::{JsAnyExpression, JsConditionalExpression, JsSyntaxKind, JsSyntaxNode}; +use rome_js_syntax::{JsConditionalExpression, JsSyntaxKind, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsConditionalExpression; @@ -42,18 +42,6 @@ impl NeedsParentheses for JsConditionalExpression { } } -impl ExpressionNode for JsConditionalExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/function_expression.rs b/crates/rome_js_formatter/src/js/expressions/function_expression.rs index 87c649ab839..34710226385 100644 --- a/crates/rome_js_formatter/src/js/expressions/function_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/function_expression.rs @@ -2,11 +2,10 @@ use crate::prelude::*; use crate::js::declarations::function_declaration::FormatFunction; use crate::parentheses::{ - is_callee, is_first_in_statement, is_tag, ExpressionNode, FirstInStatementMode, - NeedsParentheses, + is_callee, is_first_in_statement, is_tag, FirstInStatementMode, NeedsParentheses, }; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsFunctionExpression, JsSyntaxNode}; +use rome_js_syntax::{JsFunctionExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsFunctionExpression; @@ -32,18 +31,6 @@ impl NeedsParentheses for JsFunctionExpression { } } -impl ExpressionNode for JsFunctionExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/identifier_expression.rs b/crates/rome_js_formatter/src/js/expressions/identifier_expression.rs index c5cd76d023e..51c37b150ee 100644 --- a/crates/rome_js_formatter/src/js/expressions/identifier_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/identifier_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsIdentifierExpressionFields}; +use rome_js_syntax::JsIdentifierExpressionFields; use rome_js_syntax::{JsIdentifierExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -30,15 +30,3 @@ impl NeedsParentheses for JsIdentifierExpression { false } } - -impl ExpressionNode for JsIdentifierExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/import_call_expression.rs b/crates/rome_js_formatter/src/js/expressions/import_call_expression.rs index 239748810cb..a277f6eb54b 100644 --- a/crates/rome_js_formatter/src/js/expressions/import_call_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/import_call_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsImportCallExpressionFields}; +use rome_js_syntax::JsImportCallExpressionFields; use rome_js_syntax::{JsImportCallExpression, JsSyntaxKind, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -28,15 +28,3 @@ impl NeedsParentheses for JsImportCallExpression { matches!(parent.kind(), JsSyntaxKind::JS_NEW_EXPRESSION) } } - -impl ExpressionNode for JsImportCallExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/in_expression.rs b/crates/rome_js_formatter/src/js/expressions/in_expression.rs index 2403ec5e9da..7351cbc64dd 100644 --- a/crates/rome_js_formatter/src/js/expressions/in_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/in_expression.rs @@ -1,11 +1,9 @@ use crate::prelude::*; use crate::utils::{needs_binary_like_parentheses, JsAnyBinaryLikeExpression}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; -use rome_js_syntax::{ - JsAnyExpression, JsAnyStatement, JsForStatement, JsInExpression, JsSyntaxNode, -}; +use rome_js_syntax::{JsAnyStatement, JsForStatement, JsInExpression, JsSyntaxNode}; use rome_rowan::AstNode; #[derive(Debug, Clone, Default)] @@ -31,18 +29,6 @@ impl NeedsParentheses for JsInExpression { } } -impl ExpressionNode for JsInExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - /// Add parentheses if the `in` is inside of a `for` initializer (see tests). fn is_in_for_initializer(expression: &JsInExpression) -> bool { let mut current = expression.clone().into_syntax(); diff --git a/crates/rome_js_formatter/src/js/expressions/instanceof_expression.rs b/crates/rome_js_formatter/src/js/expressions/instanceof_expression.rs index 1d55c34968d..a7183e8a8c2 100644 --- a/crates/rome_js_formatter/src/js/expressions/instanceof_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/instanceof_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use crate::utils::{needs_binary_like_parentheses, JsAnyBinaryLikeExpression}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsInstanceofExpression, JsSyntaxNode}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsInstanceofExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsInstanceofExpression; @@ -27,18 +27,6 @@ impl NeedsParentheses for JsInstanceofExpression { } } -impl ExpressionNode for JsInstanceofExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { use crate::{assert_needs_parentheses, assert_not_needs_parentheses}; diff --git a/crates/rome_js_formatter/src/js/expressions/logical_expression.rs b/crates/rome_js_formatter/src/js/expressions/logical_expression.rs index 2eaa000bd51..3e74ad1cfbf 100644 --- a/crates/rome_js_formatter/src/js/expressions/logical_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/logical_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use crate::utils::{needs_binary_like_parentheses, JsAnyBinaryLikeExpression}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsLogicalExpression, JsSyntaxNode}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsLogicalExpression, JsSyntaxNode}; use rome_rowan::AstNode; #[derive(Debug, Clone, Default)] @@ -25,29 +25,10 @@ impl FormatNodeRule for FormatJsLogicalExpression { impl NeedsParentheses for JsLogicalExpression { fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { if let Some(parent) = JsLogicalExpression::cast(parent.clone()) { - return if parent.operator() != self.operator() { - true - } else { - // TODO: Parentheses should never be needed for the same operators BUT this is causing a re-formatting - // issue if a logical expression has an in-balanced tree. See issue-7024.js for a test case.. - // The way prettier solves this is by re-balancing the tree before formatting, something, Rome' doesn't yet support. - Ok(self.syntax()) != parent.left().map(AstNode::into_syntax).as_ref() - }; + parent.operator() != self.operator() + } else { + needs_binary_like_parentheses(&JsAnyBinaryLikeExpression::from(self.clone()), parent) } - - needs_binary_like_parentheses(&JsAnyBinaryLikeExpression::from(self.clone()), parent) - } -} - -impl ExpressionNode for JsLogicalExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() } } diff --git a/crates/rome_js_formatter/src/js/expressions/new_expression.rs b/crates/rome_js_formatter/src/js/expressions/new_expression.rs index 08fbc328234..cc95edbf40e 100644 --- a/crates/rome_js_formatter/src/js/expressions/new_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/new_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsNewExpression, JsSyntaxKind}; +use rome_js_syntax::{JsNewExpression, JsSyntaxKind}; use rome_js_syntax::{JsNewExpressionFields, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -53,15 +53,3 @@ impl NeedsParentheses for JsNewExpression { matches!(parent.kind(), JsSyntaxKind::JS_EXTENDS_CLAUSE) } } - -impl ExpressionNode for JsNewExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/null_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/null_literal_expression.rs index b98f3037aa6..c6116adc619 100644 --- a/crates/rome_js_formatter/src/js/expressions/null_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/null_literal_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsNullLiteralExpressionFields}; +use rome_js_syntax::JsNullLiteralExpressionFields; use rome_js_syntax::{JsNullLiteralExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -30,15 +30,3 @@ impl NeedsParentheses for JsNullLiteralExpression { false } } - -impl ExpressionNode for JsNullLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs index 2138c228a94..c970fd08667 100644 --- a/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/number_literal_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{is_member_object, ExpressionNode, NeedsParentheses}; +use crate::parentheses::{is_member_object, NeedsParentheses}; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsNumberLiteralExpression}; +use rome_js_syntax::JsNumberLiteralExpression; use rome_js_syntax::{JsNumberLiteralExpressionFields, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -31,18 +31,6 @@ impl NeedsParentheses for JsNumberLiteralExpression { } } -impl ExpressionNode for JsNumberLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/object_expression.rs b/crates/rome_js_formatter/src/js/expressions/object_expression.rs index 5746064a641..0a58a3d8eb7 100644 --- a/crates/rome_js_formatter/src/js/expressions/object_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/object_expression.rs @@ -1,10 +1,8 @@ -use crate::parentheses::{ - is_first_in_statement, ExpressionNode, FirstInStatementMode, NeedsParentheses, -}; +use crate::parentheses::{is_first_in_statement, FirstInStatementMode, NeedsParentheses}; use crate::prelude::*; use crate::utils::JsObjectLike; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsObjectExpression, JsSyntaxKind, JsSyntaxNode}; +use rome_js_syntax::{JsObjectExpression, JsSyntaxKind, JsSyntaxNode}; #[derive(Debug, Clone, Default)] pub struct FormatJsObjectExpression; @@ -29,18 +27,6 @@ impl NeedsParentheses for JsObjectExpression { } } -impl ExpressionNode for JsObjectExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { use crate::assert_needs_parentheses; diff --git a/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs b/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs index c894357fbfd..6b773b44a84 100644 --- a/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs @@ -1,7 +1,7 @@ use crate::prelude::*; -use rome_formatter::write; +use rome_formatter::{format_args, write}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_js_syntax::{ JsAnyExpression, JsParenthesizedExpression, JsParenthesizedExpressionFields, JsSyntaxNode, }; @@ -21,16 +21,35 @@ impl FormatNodeRule for FormatJsParenthesizedExpressi r_paren_token, } = node.as_fields(); + let l_paren_token = l_paren_token?; let expression = expression?; - write!( - f, - [ - format_removed(&l_paren_token?), - expression.format(), - format_removed(&r_paren_token?) - ] - ) + let should_hug = !(expression.syntax().has_comments_direct() + || l_paren_token.has_trailing_comments()) + && (matches!( + expression, + JsAnyExpression::JsObjectExpression(_) | JsAnyExpression::JsArrayExpression(_) + )); + + if should_hug { + write!( + f, + [ + l_paren_token.format(), + expression.format(), + r_paren_token.format() + ] + ) + } else { + write!( + f, + [group(&format_args![ + l_paren_token.format(), + soft_block_indent(&expression.format()), + r_paren_token.format() + ])] + ) + } } fn needs_parentheses(&self, item: &JsParenthesizedExpression) -> bool { @@ -49,17 +68,3 @@ impl NeedsParentheses for JsParenthesizedExpression { false } } - -impl ExpressionNode for JsParenthesizedExpression { - fn resolve(&self) -> JsAnyExpression { - let inner = self.expression(); - - inner.unwrap_or_else(|_| self.clone().into()) - } - - fn into_resolved(self) -> JsAnyExpression { - let inner = self.expression(); - - inner.unwrap_or_else(|_| self.into()) - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/post_update_expression.rs b/crates/rome_js_formatter/src/js/expressions/post_update_expression.rs index 9a87865f119..67a77ac50ea 100644 --- a/crates/rome_js_formatter/src/js/expressions/post_update_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/post_update_expression.rs @@ -1,10 +1,8 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{ - unary_like_expression_needs_parentheses, ExpressionNode, NeedsParentheses, -}; -use rome_js_syntax::{JsAnyExpression, JsPostUpdateExpressionFields}; +use crate::parentheses::{unary_like_expression_needs_parentheses, NeedsParentheses}; +use rome_js_syntax::JsPostUpdateExpressionFields; use rome_js_syntax::{JsPostUpdateExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -31,18 +29,6 @@ impl NeedsParentheses for JsPostUpdateExpression { } } -impl ExpressionNode for JsPostUpdateExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/pre_update_expression.rs b/crates/rome_js_formatter/src/js/expressions/pre_update_expression.rs index 3dd67fd8346..34d574814ba 100644 --- a/crates/rome_js_formatter/src/js/expressions/pre_update_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/pre_update_expression.rs @@ -1,13 +1,10 @@ -use crate::parentheses::{ - unary_like_expression_needs_parentheses, ExpressionNode, NeedsParentheses, -}; +use crate::parentheses::{unary_like_expression_needs_parentheses, NeedsParentheses}; use crate::prelude::*; use rome_formatter::write; use rome_js_syntax::JsPreUpdateExpressionFields; use rome_js_syntax::{ - JsAnyExpression, JsPreUpdateExpression, JsPreUpdateOperator, JsSyntaxNode, JsUnaryExpression, - JsUnaryOperator, + JsPreUpdateExpression, JsPreUpdateOperator, JsSyntaxNode, JsUnaryExpression, JsUnaryOperator, }; use rome_rowan::match_ast; @@ -50,18 +47,6 @@ impl NeedsParentheses for JsPreUpdateExpression { } } -impl ExpressionNode for JsPreUpdateExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/regex_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/regex_literal_expression.rs index a1174c4bd61..27f567af8ea 100644 --- a/crates/rome_js_formatter/src/js/expressions/regex_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/regex_literal_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsRegexLiteralExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::JsRegexLiteralExpressionFields; use rome_js_syntax::{JsRegexLiteralExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -57,15 +57,3 @@ impl NeedsParentheses for JsRegexLiteralExpression { false } } - -impl ExpressionNode for JsRegexLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/sequence_expression.rs b/crates/rome_js_formatter/src/js/expressions/sequence_expression.rs index aa0cf8dab80..20aa63fc01a 100644 --- a/crates/rome_js_formatter/src/js/expressions/sequence_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/sequence_expression.rs @@ -1,10 +1,10 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::{format_args, write}; -use rome_js_syntax::JsSyntaxKind::{JS_PARENTHESIZED_EXPRESSION, JS_SEQUENCE_EXPRESSION}; +use rome_js_syntax::JsSyntaxKind::JS_SEQUENCE_EXPRESSION; use rome_js_syntax::{ - JsAnyExpression, JsSequenceExpression, JsSequenceExpressionFields, JsSyntaxKind, JsSyntaxNode, + JsSequenceExpression, JsSequenceExpressionFields, JsSyntaxKind, JsSyntaxNode, }; use rome_rowan::AstNode; @@ -26,7 +26,7 @@ impl FormatNodeRule for FormatJsSequenceExpression { for parent in node.syntax().ancestors().skip(1) { if parent.kind() == JS_SEQUENCE_EXPRESSION { is_nested = true; - } else if parent.kind() != JS_PARENTHESIZED_EXPRESSION { + } else { first_non_sequence_or_paren_parent = Some(parent); break; } @@ -89,18 +89,6 @@ impl NeedsParentheses for JsSequenceExpression { } } -impl ExpressionNode for JsSequenceExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { 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 941beca7a67..8110a73eec7 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 @@ -1,12 +1,12 @@ use crate::prelude::*; use crate::js::expressions::computed_member_expression::JsAnyComputedMemberLike; -use crate::parentheses::{resolve_parent, AssignmentNode, ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::{format_args, write}; use rome_js_syntax::{ JsAnyAssignment, JsAnyAssignmentPattern, JsAnyExpression, JsAnyName, JsAssignmentExpression, - JsInitializerClause, JsParenthesizedAssignment, JsParenthesizedExpression, - JsStaticMemberAssignment, JsStaticMemberExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, + JsInitializerClause, JsStaticMemberAssignment, JsStaticMemberExpression, JsSyntaxKind, + JsSyntaxNode, JsSyntaxToken, }; use rome_rowan::{declare_node_union, AstNode, SyntaxResult}; @@ -85,8 +85,8 @@ impl JsAnyStaticMemberLike { } fn layout(&self) -> SyntaxResult { - let parent = resolve_parent(self.syntax()); - let object = self.object()?.resolve(); + let parent = self.syntax().parent(); + let object = self.object()?; let is_nested = match &parent { Some(parent) => { @@ -125,16 +125,14 @@ impl JsAnyStaticMemberLike { let first_non_static_member_ancestor = self.syntax().ancestors().find(|parent| { !(JsAnyStaticMemberLike::can_cast(parent.kind()) - || JsAnyComputedMemberLike::can_cast(parent.kind()) - || JsParenthesizedExpression::can_cast(parent.kind()) - || JsParenthesizedAssignment::can_cast(parent.kind())) + || JsAnyComputedMemberLike::can_cast(parent.kind())) }); let layout = match first_non_static_member_ancestor.and_then(JsAnyExpression::cast) { Some(JsAnyExpression::JsNewExpression(_)) => StaticMemberLikeLayout::NoBreak, Some(JsAnyExpression::JsAssignmentExpression(assignment)) => { if matches!( - assignment.left()?.resolve(), + assignment.left()?, JsAnyAssignmentPattern::JsAnyAssignment( JsAnyAssignment::JsIdentifierAssignment(_) ) @@ -176,7 +174,6 @@ pub(crate) fn member_chain_callee_needs_parens( JsComputedMemberExpression(member) => member.object().ok(), JsTemplate(template) => template.tag(), TsNonNullAssertionExpression(assertion) => assertion.expression().ok(), - JsParenthesizedExpression(expression) => expression.expression().ok(), _ => None, }); @@ -186,18 +183,6 @@ pub(crate) fn member_chain_callee_needs_parens( } } -impl ExpressionNode for JsStaticMemberExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/string_literal_expression.rs b/crates/rome_js_formatter/src/js/expressions/string_literal_expression.rs index ffd6d6a8a5d..183244dc304 100644 --- a/crates/rome_js_formatter/src/js/expressions/string_literal_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/string_literal_expression.rs @@ -2,8 +2,8 @@ use crate::prelude::*; use crate::utils::{FormatLiteralStringToken, StringLiteralParentKind}; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsAnyLiteralExpression, JsStringLiteralExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::JsStringLiteralExpressionFields; use rome_js_syntax::{JsExpressionStatement, JsSyntaxKind}; use rome_js_syntax::{JsStringLiteralExpression, JsSyntaxNode}; use rome_rowan::AstNode; @@ -49,18 +49,6 @@ impl NeedsParentheses for JsStringLiteralExpression { } } -impl ExpressionNode for JsStringLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - JsAnyExpression::JsAnyLiteralExpression(JsAnyLiteralExpression::from(self)) - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/super_expression.rs b/crates/rome_js_formatter/src/js/expressions/super_expression.rs index e03e6cd7907..8282735c8d0 100644 --- a/crates/rome_js_formatter/src/js/expressions/super_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/super_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsSuperExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::JsSuperExpressionFields; use rome_js_syntax::{JsSuperExpression, JsSyntaxNode}; #[derive(Debug, Clone, Default)] @@ -30,15 +30,3 @@ impl NeedsParentheses for JsSuperExpression { false } } - -impl ExpressionNode for JsSuperExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/template.rs b/crates/rome_js_formatter/src/js/expressions/template.rs index 20c1f8cb9b9..73fd9223daa 100644 --- a/crates/rome_js_formatter/src/js/expressions/template.rs +++ b/crates/rome_js_formatter/src/js/expressions/template.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use rome_formatter::write; use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_js_syntax::{JsAnyExpression, JsSyntaxNode, JsTemplate, TsTemplateLiteralType}; use rome_js_syntax::{JsSyntaxToken, TsTypeArguments}; use rome_rowan::{declare_node_union, SyntaxResult}; @@ -93,15 +93,3 @@ impl NeedsParentheses for JsTemplate { } } } - -impl ExpressionNode for JsTemplate { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/template_element.rs b/crates/rome_js_formatter/src/js/expressions/template_element.rs index 26690031001..ac176e79ce6 100644 --- a/crates/rome_js_formatter/src/js/expressions/template_element.rs +++ b/crates/rome_js_formatter/src/js/expressions/template_element.rs @@ -4,7 +4,6 @@ use rome_formatter::{format_args, write, FormatOptions, FormatRuleWithOptions, V use crate::context::TabWidth; use crate::js::lists::template_element_list::{TemplateElementIndention, TemplateElementLayout}; -use crate::parentheses::ExpressionNode; use rome_js_syntax::{ JsAnyExpression, JsSyntaxNode, JsSyntaxToken, JsTemplateElement, TsTemplateElement, }; @@ -107,10 +106,7 @@ impl Format for FormatTemplateElement { TemplateElementLayout::Fit => { use JsAnyExpression::*; - let expression = self - .element - .expression() - .map(|expression| expression.resolve()); + let expression = self.element.expression(); // It's preferred to break after/before `${` and `}` rather than breaking in the // middle of some expressions. diff --git a/crates/rome_js_formatter/src/js/expressions/this_expression.rs b/crates/rome_js_formatter/src/js/expressions/this_expression.rs index ac6f92c59ca..246bf8b0798 100644 --- a/crates/rome_js_formatter/src/js/expressions/this_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/this_expression.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsThisExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::JsThisExpressionFields; use rome_js_syntax::{JsSyntaxNode, JsThisExpression}; #[derive(Debug, Clone, Default)] @@ -29,15 +29,3 @@ impl NeedsParentheses for JsThisExpression { false } } - -impl ExpressionNode for JsThisExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/expressions/unary_expression.rs b/crates/rome_js_formatter/src/js/expressions/unary_expression.rs index 7e132f992dc..5e54eebca4c 100644 --- a/crates/rome_js_formatter/src/js/expressions/unary_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/unary_expression.rs @@ -1,12 +1,10 @@ use crate::prelude::*; -use rome_formatter::write; +use rome_formatter::{format_args, write}; -use crate::parentheses::{ - unary_like_expression_needs_parentheses, ExpressionNode, NeedsParentheses, -}; +use crate::parentheses::{unary_like_expression_needs_parentheses, NeedsParentheses}; -use rome_js_syntax::JsUnaryExpression; -use rome_js_syntax::{JsAnyExpression, JsSyntaxNode}; +use rome_js_syntax::JsSyntaxNode; +use rome_js_syntax::{JsSyntaxKind, JsUnaryExpression}; use rome_js_syntax::{JsUnaryExpressionFields, JsUnaryOperator}; use rome_rowan::match_ast; @@ -35,7 +33,18 @@ impl FormatNodeRule for FormatJsUnaryExpression { write!(f, [space()])?; } - write![f, [argument.format(),]] + if argument.syntax().has_leading_comments() { + write!( + f, + [group(&format_args![ + format_inserted(JsSyntaxKind::L_PAREN), + soft_block_indent(&argument.format()), + format_inserted(JsSyntaxKind::R_PAREN) + ])] + ) + } else { + write![f, [argument.format()]] + } } fn needs_parentheses(&self, item: &JsUnaryExpression) -> bool { @@ -62,18 +71,6 @@ impl NeedsParentheses for JsUnaryExpression { } } -impl ExpressionNode for JsUnaryExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/expressions/yield_expression.rs b/crates/rome_js_formatter/src/js/expressions/yield_expression.rs index ba12871d163..e1a8a010e0c 100644 --- a/crates/rome_js_formatter/src/js/expressions/yield_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/yield_expression.rs @@ -2,8 +2,8 @@ use crate::prelude::*; use rome_formatter::write; use crate::js::expressions::await_expression::await_or_yield_needs_parens; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{JsAnyExpression, JsSyntaxKind, JsYieldExpressionFields}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsSyntaxKind, JsYieldExpressionFields}; use rome_js_syntax::{JsSyntaxNode, JsYieldExpression}; #[derive(Debug, Clone, Default)] @@ -31,18 +31,6 @@ impl NeedsParentheses for JsYieldExpression { } } -impl ExpressionNode for JsYieldExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/js/lists/template_element_list.rs b/crates/rome_js_formatter/src/js/lists/template_element_list.rs index e2761d03c70..bbeebef3dcc 100644 --- a/crates/rome_js_formatter/src/js/lists/template_element_list.rs +++ b/crates/rome_js_formatter/src/js/lists/template_element_list.rs @@ -165,7 +165,6 @@ fn is_simple_member_expression(expression: JsAnyExpression) -> SyntaxResult expression.expression()?, JsAnyExpression::JsIdentifierExpression(_) | JsAnyExpression::JsThisExpression(_) => { return Ok(true); } diff --git a/crates/rome_js_formatter/src/js/module/import_meta.rs b/crates/rome_js_formatter/src/js/module/import_meta.rs index eec52142bb6..ffe25eb51e0 100644 --- a/crates/rome_js_formatter/src/js/module/import_meta.rs +++ b/crates/rome_js_formatter/src/js/module/import_meta.rs @@ -1,9 +1,9 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; +use rome_js_syntax::ImportMetaFields; use rome_js_syntax::{ImportMeta, JsSyntaxNode}; -use rome_js_syntax::{ImportMetaFields, JsAnyExpression}; #[derive(Debug, Clone, Default)] pub struct FormatImportMeta; @@ -43,15 +43,3 @@ impl NeedsParentheses for ImportMeta { false } } - -impl ExpressionNode for ImportMeta { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/js/statements/return_statement.rs b/crates/rome_js_formatter/src/js/statements/return_statement.rs index 1eed0b8e7d0..1493da0fb9e 100644 --- a/crates/rome_js_formatter/src/js/statements/return_statement.rs +++ b/crates/rome_js_formatter/src/js/statements/return_statement.rs @@ -1,13 +1,11 @@ use crate::prelude::*; -use crate::utils::{FormatWithSemicolon, JsAnyBinaryLikeExpression, JsAnyBinaryLikeLeftExpression}; +use crate::utils::{FormatWithSemicolon, JsAnyBinaryLikeExpression}; use rome_formatter::{format_args, write}; -use crate::parentheses::get_expression_left_side; use rome_js_syntax::{ - JsAnyExpression, JsReturnStatement, JsReturnStatementFields, JsSequenceExpression, + JsAnyExpression, JsReturnStatement, JsReturnStatementFields, JsSequenceExpression, JsSyntaxKind, }; -use rome_rowan::SyntaxResult; #[derive(Debug, Clone, Default)] pub struct FormatJsReturnStatement; @@ -52,19 +50,16 @@ impl Format for FormatReturnOrThrowArgument<'_> { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { let argument = self.0; - if has_argument_leading_comments(argument)? { - let syntax = argument.syntax(); - let first_token = syntax.first_token(); - let last_token = syntax.last_token(); + if has_argument_leading_comments(argument) { write!( f, - [format_parenthesize( - first_token.as_ref(), + [ + format_inserted(JsSyntaxKind::L_PAREN), &block_indent(&argument.format()), - last_token.as_ref() - )] + format_inserted(JsSyntaxKind::R_PAREN) + ] ) - } else if is_binary_or_sequence_argument(argument)? { + } else if is_binary_or_sequence_argument(argument) { write!( f, [group(&format_args![ @@ -85,37 +80,16 @@ impl Format for FormatReturnOrThrowArgument<'_> { /// /// Traversing the left nodes is necessary in case the first node is parenthesized because /// parentheses will be removed (and be re-added by the return statement, but only if the argument breaks) -fn has_argument_leading_comments(argument: &JsAnyExpression) -> SyntaxResult { +fn has_argument_leading_comments(argument: &JsAnyExpression) -> bool { if matches!(argument, JsAnyExpression::JsxTagExpression(_)) { // JSX formatting takes care of adding parens - return Ok(false); + return false; } - if argument.syntax().has_leading_comments() { - return Ok(true); - } - - let result = match get_expression_left_side(argument) { - Some(JsAnyBinaryLikeLeftExpression::JsAnyExpression(expression)) => { - has_argument_leading_comments(&expression)? - } - Some(JsAnyBinaryLikeLeftExpression::JsPrivateName(name)) => { - name.syntax().has_leading_comments() - } - None => false, - }; - - Ok(result) + argument.syntax().has_leading_comments() } -fn is_binary_or_sequence_argument(argument: &JsAnyExpression) -> SyntaxResult { - if JsSequenceExpression::can_cast(argument.syntax().kind()) +fn is_binary_or_sequence_argument(argument: &JsAnyExpression) -> bool { + JsSequenceExpression::can_cast(argument.syntax().kind()) || JsAnyBinaryLikeExpression::can_cast(argument.syntax().kind()) - { - Ok(true) - } else if let JsAnyExpression::JsParenthesizedExpression(inner) = argument { - is_binary_or_sequence_argument(&inner.expression()?) - } else { - Ok(false) - } } diff --git a/crates/rome_js_formatter/src/js/unknown/unknown_assignment.rs b/crates/rome_js_formatter/src/js/unknown/unknown_assignment.rs index 204f9f5cdf9..4cf01d7f3c6 100644 --- a/crates/rome_js_formatter/src/js/unknown/unknown_assignment.rs +++ b/crates/rome_js_formatter/src/js/unknown/unknown_assignment.rs @@ -1,9 +1,7 @@ use crate::prelude::*; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; -use rome_js_syntax::{ - JsAnyAssignment, JsAnyAssignmentPattern, JsSyntaxKind, JsSyntaxNode, JsUnknownAssignment, -}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsSyntaxNode, JsUnknownAssignment}; use rome_rowan::AstNode; #[derive(Debug, Clone, Default)] @@ -23,22 +21,14 @@ impl FormatNodeRule for FormatJsUnknownAssignment { } } -impl AssignmentNode for JsUnknownAssignment { +impl NeedsParentheses for JsUnknownAssignment { #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) + fn needs_parentheses(&self) -> bool { + false } #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - -impl NeedsParentheses for JsUnknownAssignment { fn needs_parentheses_with_parent(&self, _: &JsSyntaxNode) -> bool { - self.syntax().parent().map_or(false, |parent| { - parent.kind() == JsSyntaxKind::JS_PARENTHESIZED_ASSIGNMENT - }) + false } } diff --git a/crates/rome_js_formatter/src/js/unknown/unknown_expression.rs b/crates/rome_js_formatter/src/js/unknown/unknown_expression.rs index c00eaedf3bc..fa8b3386e7d 100644 --- a/crates/rome_js_formatter/src/js/unknown/unknown_expression.rs +++ b/crates/rome_js_formatter/src/js/unknown/unknown_expression.rs @@ -1,9 +1,7 @@ use crate::prelude::*; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; -use rome_js_syntax::{ - JsAnyExpression, JsParenthesizedExpression, JsSyntaxNode, JsUnknownExpression, -}; +use crate::parentheses::NeedsParentheses; +use rome_js_syntax::{JsSyntaxNode, JsUnknownExpression}; use rome_rowan::AstNode; #[derive(Debug, Clone, Default)] @@ -24,26 +22,13 @@ impl FormatNodeRule for FormatJsUnknownExpression { } impl NeedsParentheses for JsUnknownExpression { + #[inline] fn needs_parentheses(&self) -> bool { - // Keep parens if it is parenthesized. - self.syntax().parent().map_or(false, |parent| { - JsParenthesizedExpression::can_cast(parent.kind()) - }) + false } + #[inline] fn needs_parentheses_with_parent(&self, _parent: &JsSyntaxNode) -> bool { self.needs_parentheses() } } - -impl ExpressionNode for JsUnknownExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/jsx/expressions/tag_expression.rs b/crates/rome_js_formatter/src/jsx/expressions/tag_expression.rs index a281168cb01..cf97d0b02df 100644 --- a/crates/rome_js_formatter/src/jsx/expressions/tag_expression.rs +++ b/crates/rome_js_formatter/src/jsx/expressions/tag_expression.rs @@ -1,11 +1,11 @@ -use crate::parentheses::{is_callee, is_tag, ExpressionNode, NeedsParentheses}; +use crate::parentheses::{is_callee, is_tag, NeedsParentheses}; use crate::prelude::*; use crate::utils::jsx::{get_wrap_state, WrapState}; use rome_formatter::{format_args, write}; use rome_js_syntax::{ - JsAnyExpression, JsBinaryExpression, JsBinaryOperator, JsSyntaxKind, JsSyntaxNode, - JsxTagExpression, + JsBinaryExpression, JsBinaryOperator, JsSyntaxKind, JsSyntaxNode, JsxTagExpression, }; +use rome_rowan::AstNode; #[derive(Debug, Clone, Default)] pub struct FormatJsxTagExpression; @@ -36,11 +36,7 @@ impl NeedsParentheses for JsxTagExpression { JsSyntaxKind::JS_BINARY_EXPRESSION => { let binary = JsBinaryExpression::unwrap_cast(parent.clone()); - let is_left = binary - .left() - .map(ExpressionNode::into_resolved_syntax) - .as_ref() - == Ok(self.syntax()); + let is_left = binary.left().map(AstNode::into_syntax).as_ref() == Ok(self.syntax()); matches!(binary.operator(), Ok(JsBinaryOperator::LessThan)) && is_left } JsSyntaxKind::TS_AS_EXPRESSION @@ -58,15 +54,3 @@ impl NeedsParentheses for JsxTagExpression { } } } - -impl ExpressionNode for JsxTagExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} diff --git a/crates/rome_js_formatter/src/lib.rs b/crates/rome_js_formatter/src/lib.rs index a97c49fe8c0..2f877af77b4 100644 --- a/crates/rome_js_formatter/src/lib.rs +++ b/crates/rome_js_formatter/src/lib.rs @@ -248,19 +248,32 @@ pub mod prelude; mod ts; pub mod utils; +#[cfg(test)] +mod check_reformat; +#[rustfmt::skip] +mod generated; +pub(crate) mod builders; +pub mod context; +mod parentheses; +pub(crate) mod separated; +mod syntax_rewriter; + use rome_formatter::prelude::*; -use rome_formatter::{write, Comments, CstFormatContext, FormatLanguage}; +use rome_formatter::{ + write, Comments, CstFormatContext, Format, FormatLanguage, TransformSourceMap, +}; use rome_formatter::{Buffer, FormatOwnedWithRule, FormatRefWithRule, Formatted, Printed}; use rome_js_syntax::{ JsAnyDeclaration, JsAnyStatement, JsLanguage, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, }; -use rome_rowan::AstNode; use rome_rowan::SyntaxResult; use rome_rowan::TextRange; +use rome_rowan::{AstNode, SyntaxNode}; use crate::builders::{format_parenthesize, format_suppressed_node}; use crate::context::{JsCommentStyle, JsFormatContext, JsFormatOptions}; use crate::cst::FormatJsSyntaxNode; +use crate::syntax_rewriter::transform; use std::iter::FusedIterator; use std::marker::PhantomData; @@ -492,6 +505,13 @@ impl FormatLanguage for JsFormatLanguage { JsCommentStyle } + fn transform( + &self, + root: &SyntaxNode, + ) -> Option<(SyntaxNode, TransformSourceMap)> { + Some(transform(root.clone())) + } + fn is_range_formatting_node(&self, node: &JsSyntaxNode) -> bool { let kind = node.kind(); @@ -512,8 +532,12 @@ impl FormatLanguage for JsFormatLanguage { &self.options } - fn create_context(self, comments: Comments) -> Self::Context { - JsFormatContext::new(self.options, comments) + fn create_context( + self, + comments: Comments, + source_map: Option, + ) -> Self::Context { + JsFormatContext::new(self.options, comments).with_source_map(source_map) } } @@ -563,13 +587,16 @@ pub fn format_sub_tree(options: JsFormatOptions, root: &JsSyntaxNode) -> FormatR #[cfg(test)] mod tests { - use super::format_range; + use super::{format_node, format_range}; - use crate::JsFormatOptions; + use crate::context::JsFormatOptions; use rome_formatter::IndentStyle; - use rome_js_parser::parse_script; + use rome_js_parser::{parse, parse_script}; + use rome_js_syntax::SourceType; use rome_rowan::{TextRange, TextSize}; + use crate::check_reformat::{check_reformat, CheckReformatParams}; + #[test] fn test_range_formatting() { let input = " @@ -715,23 +742,6 @@ function() { assert_eq!(result.as_code(), ""); assert_eq!(result.range(), Some(TextRange::new(range_start, range_end))); } -} - -#[cfg(test)] -mod check_reformat; -#[rustfmt::skip] -mod generated; -pub(crate) mod builders; -pub mod context; -mod parentheses; -pub(crate) mod separated; - -#[cfg(test)] -mod test { - use crate::check_reformat::{check_reformat, CheckReformatParams}; - use crate::{format_node, format_range, JsFormatOptions}; - use rome_js_parser::parse; - use rome_js_syntax::{SourceType, TextRange, TextSize}; #[ignore] #[test] @@ -744,7 +754,9 @@ test.expect(t => { "#; let syntax = SourceType::tsx(); let tree = parse(src, 0, syntax); - let result = format_node(JsFormatOptions::default(), &tree.syntax()) + let options = JsFormatOptions::default(); + + let result = format_node(options.clone(), &tree.syntax()) .unwrap() .print(); check_reformat(CheckReformatParams { @@ -752,7 +764,7 @@ test.expect(t => { text: result.as_code(), source_type: syntax, file_name: "quick_test", - options: JsFormatOptions::default(), + options, }); assert_eq!( result.as_code(), diff --git a/crates/rome_js_formatter/src/parentheses.rs b/crates/rome_js_formatter/src/parentheses.rs index 04acd791618..45ff5eb30db 100644 --- a/crates/rome_js_formatter/src/parentheses.rs +++ b/crates/rome_js_formatter/src/parentheses.rs @@ -8,7 +8,7 @@ //! //! The [NeedsParentheses] trait forms the foundation of Rome's parentheses formatting and is implemented //! by all nodes supporting parentheses (expressions, assignments, and types). The trait's main method -//! is the [`needs_parentheses`](NeedsParentheses::needs_parentheses) +//! is the [NeedsParentheses::needs_parentheses] //! method that implements the rules when a node requires parentheses. //! A node requires parentheses to: //! * improve readability: `a << b << 3` is harder to read than `(a << b) << 3` @@ -22,27 +22,20 @@ //! //! There are two measures taken by Rome to ensure formatting is stable regardless of the number of parenthesized nodes in a tree: //! -//! ## Removing and adding of parentheses -//! The [FormatNodeRule](rome_js_formatter::FormatNodeRule) always inserts parentheses around a node if the rules `needs_parentheses` method -//! returns `true`. This by itself would result in the formatter adding an extra pair of parentheses with every format pass for nodes where parentheses are necessary. -//! This is why the [rome_js_formatter::FormatJsParenthesizedExpression] rule always removes the parentheses and relies on the -//! [FormatNodeRule](rome_js_formatter::FormatNodeRule) to add the parentheses again when necessary. +//! ## Removing parenthesized nodes //! -//! ## Testing for a a child or parent node. +//! The JavaScript formatter [pre-processes](crate:JsFormatSyntaxRewriter] the input CST and removes all parenthesized expressions, assignments, and types except if: +//! * The parenthesized node has a syntax error (skipped token trivia, missing inner expression) +//! * The node has a directly preceding closure type cast comment +//! * The inner expression is an unknown node //! -//! There are many places where a formatting rule applies different formatting depending on the type of a -//! child node or parent node. The decision taken by these rules shouldn't differ just because a node happens to be parenthesized -//! because doing so would yield different results if the formatter removes the parentheses in the first pass. +//! Removing the parenthesized nodes has the benefit that a input tree with parentheses and an input tree +//! without parentheses have the same structure for as far as the formatter is concerned and thus, +//! the formatter makes the same decisions for both trees. //! -//! The [NeedsParentheses] trait offers a [`resolve_parent`](NeedsParentheses::resolve_parent] method -//! that returns the first parent of a node that isn't parenthesized. -//! For example, calling [JsSyntaxNode::parent] on the `a` identifier in `(a).b` returns the [JsParenthesizedExpression](rome_js_syntax::JsParenthesizedExpression) -//! but calling [`resolve_parent`](NeedsParentheses::resolve_parent] returns the [JsStaticMemberExpression](rome_js_syntax::JsStaticMemberExpression). -//! -//! This module further offers node specific traits like [ExpressionNode] that implement additional methods to resolve a node. -//! Calling [`resolve`](ExpressionNode::resolve) returns the node itself if it isn't a [JsParenthesizedExpression](rome_js_syntax::JsParenthesizedExpression) -//! or traverses down the parenthesized expression and returns the first non [JsParenthesizedExpression](rome_js_syntax::JsParenthesizedExpression) node. -//! For example, calling resolve on `a` returns `a` but calling resolve on `((a))` also returns `a`. +//! ## Parentheses insertion +//! The parentheses that get removed by the pre-processing step are re-inserted by the [crate::FormatNodeRule]. +//! The rule inserts parentheses for each node where [crate::FormatNodeRule::needs_parentheses] returns true. use crate::utils::{JsAnyBinaryLikeExpression, JsAnyBinaryLikeLeftExpression}; @@ -50,67 +43,26 @@ use rome_js_syntax::{ JsAnyAssignment, JsAnyAssignmentPattern, JsAnyExpression, JsAnyFunctionBody, JsAnyLiteralExpression, JsArrowFunctionExpression, JsAssignmentExpression, JsBinaryExpression, JsBinaryOperator, JsComputedMemberAssignment, JsComputedMemberExpression, - JsConditionalExpression, JsLanguage, JsSequenceExpression, JsStaticMemberAssignment, - JsStaticMemberExpression, JsSyntaxKind, JsSyntaxNode, + JsConditionalExpression, JsLanguage, JsParenthesizedAssignment, JsParenthesizedExpression, + JsSequenceExpression, JsStaticMemberAssignment, JsStaticMemberExpression, JsSyntaxKind, + JsSyntaxNode, JsSyntaxToken, }; -use rome_rowan::{match_ast, AstNode}; +use rome_rowan::{declare_node_union, match_ast, AstNode, SyntaxResult}; /// Node that may be parenthesized to ensure it forms valid syntax or to improve readability pub trait NeedsParentheses: AstNode { fn needs_parentheses(&self) -> bool { - self.resolve_parent() + self.syntax() + .parent() .map_or(false, |parent| self.needs_parentheses_with_parent(&parent)) } - fn resolve_parent(&self) -> Option { - resolve_parent(self.syntax()) - } - /// Returns `true` if this node requires parentheses to form valid syntax or improve readability. /// /// Returns `false` if the parentheses can be omitted safely without changing semantics. fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool; } -/// Trait implemented by all JavaScript expressions. -pub trait ExpressionNode: NeedsParentheses { - /// Resolves an expression to the first non parenthesized expression. - fn resolve(&self) -> JsAnyExpression; - - /// Consumes `self` and returns the first expression that isn't a parenthesized expression. - fn into_resolved(self) -> JsAnyExpression; - - /// Resolves an expression to the first non parenthesized expression and returns its [JsSyntaxNode]. - fn resolve_syntax(&self) -> JsSyntaxNode { - self.resolve().into_syntax() - } - - /// Consumes `self` and returns the [JsSyntaxNode] of the first expression that isn't a parenthesized expression. - fn into_resolved_syntax(self) -> JsSyntaxNode { - self.into_resolved().into_syntax() - } -} - -/// Resolves to the first parent that isn't a parenthesized expression, assignment, or type. -pub(crate) fn resolve_parent(node: &JsSyntaxNode) -> Option { - let mut current = node.parent(); - - while let Some(parent) = current { - if matches!( - parent.kind(), - JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION - | JsSyntaxKind::JS_PARENTHESIZED_ASSIGNMENT - | JsSyntaxKind::TS_PARENTHESIZED_TYPE - ) { - current = parent.parent(); - } else { - return Some(parent); - } - } - - None -} - impl NeedsParentheses for JsAnyLiteralExpression { #[inline] fn needs_parentheses(&self) -> bool { @@ -157,38 +109,6 @@ impl NeedsParentheses for JsAnyLiteralExpression { } } -impl ExpressionNode for JsAnyLiteralExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - match self { - JsAnyLiteralExpression::JsBigIntLiteralExpression(big_int) => big_int.resolve(), - JsAnyLiteralExpression::JsBooleanLiteralExpression(boolean) => boolean.resolve(), - JsAnyLiteralExpression::JsNullLiteralExpression(null_literal) => null_literal.resolve(), - JsAnyLiteralExpression::JsNumberLiteralExpression(number_literal) => { - number_literal.resolve() - } - JsAnyLiteralExpression::JsRegexLiteralExpression(regex) => regex.resolve(), - JsAnyLiteralExpression::JsStringLiteralExpression(string) => string.resolve(), - } - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - match self { - JsAnyLiteralExpression::JsBigIntLiteralExpression(big_int) => big_int.into_resolved(), - JsAnyLiteralExpression::JsBooleanLiteralExpression(boolean) => boolean.into_resolved(), - JsAnyLiteralExpression::JsNullLiteralExpression(null_literal) => { - null_literal.into_resolved() - } - JsAnyLiteralExpression::JsNumberLiteralExpression(number_literal) => { - number_literal.into_resolved() - } - JsAnyLiteralExpression::JsRegexLiteralExpression(regex) => regex.into_resolved(), - JsAnyLiteralExpression::JsStringLiteralExpression(string) => string.into_resolved(), - } - } -} - impl NeedsParentheses for JsAnyExpression { fn needs_parentheses(&self) -> bool { match self { @@ -335,98 +255,6 @@ impl NeedsParentheses for JsAnyExpression { } } -impl ExpressionNode for JsAnyExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - match self { - JsAnyExpression::ImportMeta(meta) => meta.resolve(), - JsAnyExpression::JsAnyLiteralExpression(literal) => literal.resolve(), - JsAnyExpression::JsArrayExpression(array) => array.resolve(), - JsAnyExpression::JsArrowFunctionExpression(arrow) => arrow.resolve(), - JsAnyExpression::JsAssignmentExpression(assignment) => assignment.resolve(), - JsAnyExpression::JsAwaitExpression(await_expression) => await_expression.resolve(), - JsAnyExpression::JsBinaryExpression(binary) => binary.resolve(), - JsAnyExpression::JsCallExpression(call) => call.resolve(), - JsAnyExpression::JsClassExpression(class) => class.resolve(), - JsAnyExpression::JsComputedMemberExpression(member) => member.resolve(), - JsAnyExpression::JsConditionalExpression(conditional) => conditional.resolve(), - JsAnyExpression::JsFunctionExpression(function) => function.resolve(), - JsAnyExpression::JsIdentifierExpression(identifier) => identifier.resolve(), - JsAnyExpression::JsImportCallExpression(import_call) => import_call.resolve(), - JsAnyExpression::JsInExpression(in_expression) => in_expression.resolve(), - JsAnyExpression::JsInstanceofExpression(instanceof) => instanceof.resolve(), - JsAnyExpression::JsLogicalExpression(logical) => logical.resolve(), - JsAnyExpression::JsNewExpression(new) => new.resolve(), - JsAnyExpression::JsObjectExpression(object) => object.resolve(), - JsAnyExpression::JsParenthesizedExpression(parenthesized) => parenthesized.resolve(), - JsAnyExpression::JsPostUpdateExpression(update) => update.resolve(), - JsAnyExpression::JsPreUpdateExpression(update) => update.resolve(), - JsAnyExpression::JsSequenceExpression(sequence) => sequence.resolve(), - JsAnyExpression::JsStaticMemberExpression(member) => member.resolve(), - JsAnyExpression::JsSuperExpression(sup) => sup.resolve(), - JsAnyExpression::JsTemplate(template) => template.resolve(), - JsAnyExpression::JsThisExpression(this) => this.resolve(), - JsAnyExpression::JsUnaryExpression(unary) => unary.resolve(), - JsAnyExpression::JsUnknownExpression(unknown) => unknown.resolve(), - JsAnyExpression::JsYieldExpression(yield_expression) => yield_expression.resolve(), - JsAnyExpression::JsxTagExpression(jsx) => jsx.resolve(), - JsAnyExpression::NewTarget(target) => target.resolve(), - JsAnyExpression::TsAsExpression(as_expression) => as_expression.resolve(), - JsAnyExpression::TsNonNullAssertionExpression(non_null) => non_null.resolve(), - JsAnyExpression::TsTypeAssertionExpression(type_assertion) => type_assertion.resolve(), - } - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - match self { - JsAnyExpression::ImportMeta(meta) => meta.into_resolved(), - JsAnyExpression::JsAnyLiteralExpression(literal) => literal.into_resolved(), - JsAnyExpression::JsArrayExpression(array) => array.into_resolved(), - JsAnyExpression::JsArrowFunctionExpression(arrow) => arrow.into_resolved(), - JsAnyExpression::JsAssignmentExpression(assignment) => assignment.into_resolved(), - JsAnyExpression::JsAwaitExpression(await_expression) => { - await_expression.into_resolved() - } - JsAnyExpression::JsBinaryExpression(binary) => binary.into_resolved(), - JsAnyExpression::JsCallExpression(call) => call.into_resolved(), - JsAnyExpression::JsClassExpression(class) => class.into_resolved(), - JsAnyExpression::JsComputedMemberExpression(member) => member.into_resolved(), - JsAnyExpression::JsConditionalExpression(conditional) => conditional.into_resolved(), - JsAnyExpression::JsFunctionExpression(function) => function.into_resolved(), - JsAnyExpression::JsIdentifierExpression(identifier) => identifier.into_resolved(), - JsAnyExpression::JsImportCallExpression(import_call) => import_call.into_resolved(), - JsAnyExpression::JsInExpression(in_expression) => in_expression.into_resolved(), - JsAnyExpression::JsInstanceofExpression(instanceof) => instanceof.into_resolved(), - JsAnyExpression::JsLogicalExpression(logical) => logical.into_resolved(), - JsAnyExpression::JsNewExpression(new) => new.into_resolved(), - JsAnyExpression::JsObjectExpression(object) => object.into_resolved(), - JsAnyExpression::JsParenthesizedExpression(parenthesized) => { - parenthesized.into_resolved() - } - JsAnyExpression::JsPostUpdateExpression(update) => update.into_resolved(), - JsAnyExpression::JsPreUpdateExpression(update) => update.into_resolved(), - JsAnyExpression::JsSequenceExpression(sequence) => sequence.into_resolved(), - JsAnyExpression::JsStaticMemberExpression(member) => member.into_resolved(), - JsAnyExpression::JsSuperExpression(sup) => sup.into_resolved(), - JsAnyExpression::JsTemplate(template) => template.into_resolved(), - JsAnyExpression::JsThisExpression(this) => this.into_resolved(), - JsAnyExpression::JsUnaryExpression(unary) => unary.into_resolved(), - JsAnyExpression::JsUnknownExpression(unknown) => unknown.into_resolved(), - JsAnyExpression::JsYieldExpression(yield_expression) => { - yield_expression.into_resolved() - } - JsAnyExpression::JsxTagExpression(jsx) => jsx.into_resolved(), - JsAnyExpression::NewTarget(target) => target.into_resolved(), - JsAnyExpression::TsAsExpression(as_expression) => as_expression.into_resolved(), - JsAnyExpression::TsNonNullAssertionExpression(non_null) => non_null.into_resolved(), - JsAnyExpression::TsTypeAssertionExpression(type_assertion) => { - type_assertion.into_resolved() - } - } - } -} - /// Returns the left most expression of `expression`. /// /// For example, returns `a` for `(a ? b : c) + d` because it first resolves the @@ -459,7 +287,6 @@ pub(crate) fn get_expression_left_side( use JsAnyExpression::*; let left_expression = match expression { - JsParenthesizedExpression(parenthesized) => parenthesized.expression().ok(), JsSequenceExpression(sequence) => sequence.left().ok(), JsStaticMemberExpression(member) => member.object().ok(), JsComputedMemberExpression(member) => member.object().ok(), @@ -500,15 +327,13 @@ pub(crate) fn is_first_in_statement(node: JsSyntaxNode, mode: FirstInStatementMo return true; } - JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION - | JsSyntaxKind::JS_STATIC_MEMBER_EXPRESSION + JsSyntaxKind::JS_STATIC_MEMBER_EXPRESSION | JsSyntaxKind::JS_STATIC_MEMBER_ASSIGNMENT | JsSyntaxKind::JS_TEMPLATE | JsSyntaxKind::JS_CALL_EXPRESSION | JsSyntaxKind::JS_NEW_EXPRESSION | JsSyntaxKind::TS_AS_EXPRESSION - | JsSyntaxKind::TS_NON_NULL_ASSERTION_EXPRESSION - | JsSyntaxKind::JS_PARENTHESIZED_ASSIGNMENT => parent, + | JsSyntaxKind::TS_NON_NULL_ASSERTION_EXPRESSION => parent, JsSyntaxKind::JS_SEQUENCE_EXPRESSION => { let sequence = JsSequenceExpression::unwrap_cast(parent); @@ -638,11 +463,7 @@ pub(crate) fn unary_like_expression_needs_parentheses( if let Some(binary) = JsBinaryExpression::cast_ref(parent) { matches!(binary.operator(), Ok(JsBinaryOperator::Exponent)) - && binary - .left() - .map(ExpressionNode::into_resolved_syntax) - .as_ref() - == Ok(expression) + && binary.left().map(AstNode::into_syntax).as_ref() == Ok(expression) } else { update_or_lower_expression_needs_parentheses(expression, parent) } @@ -685,14 +506,14 @@ pub(crate) fn is_member_object(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bo JsComputedMemberExpression(member_expression) => { member_expression .object() - .map(ExpressionNode::into_resolved_syntax) + .map(AstNode::into_syntax) .as_ref() == Ok(node) }, JsComputedMemberAssignment(assignment) => { assignment .object() - .map(ExpressionNode::into_resolved_syntax) + .map(AstNode::into_syntax) .as_ref() == Ok(node) }, @@ -728,7 +549,7 @@ pub(crate) fn is_conditional_test(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> JsConditionalExpression(conditional) => { conditional .test() - .map(ExpressionNode::into_resolved_syntax) + .map(AstNode::into_syntax) .as_ref() == Ok(node) }, @@ -745,7 +566,7 @@ pub(crate) fn is_arrow_function_body(node: &JsSyntaxNode, parent: &JsSyntaxNode) JsArrowFunctionExpression(arrow) => { match arrow.body() { Ok(JsAnyFunctionBody::JsAnyExpression(expression)) => { - &expression.resolve_syntax() == node + expression.syntax() == node } _ => false, } @@ -776,61 +597,45 @@ pub(crate) fn is_spread(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { ) } -/// Returns `true` if `parent` is a [JsAnyBinaryLikeExpression] and `node` is the `left` or `right` of that expression. -pub(crate) fn is_binary_like_left_or_right(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { - debug_assert_is_expression(node); - debug_assert_is_parent(node, parent); - - JsAnyBinaryLikeExpression::can_cast(parent.kind()) +declare_node_union! { + pub(crate) JsAnyParenthesized = JsParenthesizedExpression | JsParenthesizedAssignment } -/// Trait implemented by all JavaScript assignments. -pub trait AssignmentNode: NeedsParentheses { - /// Resolves an assignment to the first non parenthesized assignment. - fn resolve(&self) -> JsAnyAssignmentPattern; - - /// Consumes `self` and returns the first assignment that isn't a parenthesized assignment. - fn into_resolved(self) -> JsAnyAssignmentPattern; - - /// Resolves an assignment to the first non parenthesized assignment and returns its [JsSyntaxNode]. - fn resolve_syntax(&self) -> JsSyntaxNode { - self.resolve().into_syntax() - } - - /// Consumes `self` and returns the [JsSyntaxNode] of the first assignment that isn't a assignment expression. - fn into_resolved_syntax(self) -> JsSyntaxNode { - self.into_resolved().into_syntax() +impl JsAnyParenthesized { + pub(crate) fn l_paren_token(&self) -> SyntaxResult { + match self { + JsAnyParenthesized::JsParenthesizedExpression(expression) => expression.l_paren_token(), + JsAnyParenthesized::JsParenthesizedAssignment(assignment) => assignment.l_paren_token(), + } } -} -impl AssignmentNode for JsAnyAssignment { - fn resolve(&self) -> JsAnyAssignmentPattern { + pub(crate) fn inner(&self) -> SyntaxResult { match self { - JsAnyAssignment::JsComputedMemberAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::JsIdentifierAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::JsParenthesizedAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::JsStaticMemberAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::JsUnknownAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::TsAsAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::TsNonNullAssertionAssignment(assignment) => assignment.resolve(), - JsAnyAssignment::TsTypeAssertionAssignment(assignment) => assignment.resolve(), + JsAnyParenthesized::JsParenthesizedExpression(expression) => { + expression.expression().map(AstNode::into_syntax) + } + JsAnyParenthesized::JsParenthesizedAssignment(assignment) => { + assignment.assignment().map(AstNode::into_syntax) + } } } - fn into_resolved(self) -> JsAnyAssignmentPattern { + pub(crate) fn r_paren_token(&self) -> SyntaxResult { match self { - JsAnyAssignment::JsComputedMemberAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::JsIdentifierAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::JsParenthesizedAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::JsStaticMemberAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::JsUnknownAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::TsAsAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::TsNonNullAssertionAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignment::TsTypeAssertionAssignment(assignment) => assignment.into_resolved(), + JsAnyParenthesized::JsParenthesizedExpression(expression) => expression.r_paren_token(), + JsAnyParenthesized::JsParenthesizedAssignment(assignment) => assignment.r_paren_token(), } } } +/// Returns `true` if `parent` is a [JsAnyBinaryLikeExpression] and `node` is the `left` or `right` of that expression. +pub(crate) fn is_binary_like_left_or_right(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { + debug_assert_is_expression(node); + debug_assert_is_parent(node, parent); + + JsAnyBinaryLikeExpression::can_cast(parent.kind()) +} + impl NeedsParentheses for JsAnyAssignment { fn needs_parentheses(&self) -> bool { match self { @@ -883,28 +688,6 @@ impl NeedsParentheses for JsAnyAssignment { } } -impl AssignmentNode for JsAnyAssignmentPattern { - fn resolve(&self) -> JsAnyAssignmentPattern { - match self { - JsAnyAssignmentPattern::JsAnyAssignment(assignment) => assignment.resolve(), - JsAnyAssignmentPattern::JsArrayAssignmentPattern(assignment) => assignment.resolve(), - JsAnyAssignmentPattern::JsObjectAssignmentPattern(assignment) => assignment.resolve(), - } - } - - fn into_resolved(self) -> JsAnyAssignmentPattern { - match self { - JsAnyAssignmentPattern::JsAnyAssignment(assignment) => assignment.into_resolved(), - JsAnyAssignmentPattern::JsArrayAssignmentPattern(assignment) => { - assignment.into_resolved() - } - JsAnyAssignmentPattern::JsObjectAssignmentPattern(assignment) => { - assignment.into_resolved() - } - } - } -} - impl NeedsParentheses for JsAnyAssignmentPattern { fn needs_parentheses(&self) -> bool { match self { @@ -942,7 +725,7 @@ fn debug_assert_is_expression(node: &JsSyntaxNode) { fn debug_assert_is_parent(node: &JsSyntaxNode, parent: &JsSyntaxNode) { debug_assert!( - resolve_parent(node).as_ref() == Some(parent), + node.parent().as_ref() == Some(parent), "Node {node:#?} is not a child of ${parent:#?}" ) } @@ -950,6 +733,7 @@ fn debug_assert_is_parent(node: &JsSyntaxNode, parent: &JsSyntaxNode) { #[cfg(test)] pub(crate) mod tests { use super::NeedsParentheses; + use crate::transform; use rome_js_syntax::{JsLanguage, SourceType}; use rome_rowan::AstNode; @@ -969,7 +753,8 @@ pub(crate) mod tests { ); let root = parse.syntax(); - let matching_nodes: Vec<_> = root.descendants().filter_map(T::cast).collect(); + let (transformed, _) = transform(root); + let matching_nodes: Vec<_> = transformed.descendants().filter_map(T::cast).collect(); let node = if let Some(index) = index { matching_nodes.get(index).unwrap_or_else(|| { @@ -1009,7 +794,8 @@ pub(crate) mod tests { ); let root = parse.syntax(); - let matching_nodes: Vec<_> = root.descendants().filter_map(T::cast).collect(); + let (transformed, _) = transform(root); + let matching_nodes: Vec<_> = transformed.descendants().filter_map(T::cast).collect(); let node = if let Some(index) = index { matching_nodes.get(index).unwrap_or_else(|| { diff --git a/crates/rome_js_formatter/src/syntax_rewriter.rs b/crates/rome_js_formatter/src/syntax_rewriter.rs new file mode 100644 index 00000000000..daf2c6abbf3 --- /dev/null +++ b/crates/rome_js_formatter/src/syntax_rewriter.rs @@ -0,0 +1,864 @@ +use crate::parentheses::JsAnyParenthesized; +use crate::TextRange; +use rome_formatter::{TransformSourceMap, TransformSourceMapBuilder}; +use rome_js_syntax::{ + JsAnyAssignment, JsAnyExpression, JsLanguage, JsLogicalExpression, JsSyntaxKind, JsSyntaxNode, +}; +use rome_rowan::syntax::SyntaxTrivia; +use rome_rowan::{ + AstNode, SyntaxKind, SyntaxRewriter, SyntaxTriviaPiece, SyntaxTriviaPieceComments, + VisitNodeSignal, +}; +use std::iter::FusedIterator; + +pub(super) fn transform(root: JsSyntaxNode) -> (JsSyntaxNode, TransformSourceMap) { + let mut rewriter = JsFormatSyntaxRewriter::new(&root); + let transformed = rewriter.transform(root); + (transformed, rewriter.finish()) +} + +struct JsFormatSyntaxRewriter { + source_map: TransformSourceMapBuilder, +} + +impl JsFormatSyntaxRewriter { + fn new(root: &JsSyntaxNode) -> Self { + Self { + source_map: TransformSourceMapBuilder::new(root), + } + } + + /// Replaces parenthesized expression that: + /// * have no syntax error: has no missing required child or no skipped token trivia attached to the left or right paren + /// * inner expression isn't an unknown node + /// * no closure or type cast type cast comment + /// + /// with the inner expression. + /// + /// ## Trivia Overview + /// + /// We have to spend extra attention on the handling of the trivia attached to the left and right parentheses: + /// + /// ```javascript + /// statement; + /// /* leading l-paren */ ( /* trailing l-paren */ + /// /* leading a */ a + b /* trailing b */ + /// /* leading r-paren */ ) /* trailing r-paren */ + /// ``` + /// + /// The implementation pre-appends the left parenthesis's trivia to the leading trivia of the expression's first token, + /// and appends the right parenthesis's trivia to the trailing trivia of the expression's last token. So the trivia (ignoring whitespace) + /// after the transform for the above example is: + /// + /// * `a`: `/* leading l-paren */ /* trailing l-paren */ /* leading a */` + /// * `b`: `/* trailing b */ /* leading r-paren */ /* trailing r-paren */` + /// + /// The fact that the implementation appends the right parenthesis's leading trivia to the last token's trailing + /// trivia is slightly inconsistent with our [rome_rowan::SyntaxToken::trailing_trivia] definition as it can now happen that the + /// trailing trivia contains line breaks. In practice, this isn't a problem for the formatter. + /// + /// ## Leading Whitespace Trivia + /// + /// The formatter counts the new lines in a node's leading trivia to determine if it should e.g. insert an + /// empty new line between two statements. This is why it is necessary to remove any whitespace + /// between the left parentheses and the token to avoid the insertion of additional new lines if there was a line break + /// after the left parentheses or + /// + /// ```javascript + /// a + /// ( + /// Long && + /// Longer && + /// ) + /// ``` + /// + /// would become + /// + /// ```javascript + /// a + /// + /// ( + /// Long && + /// Longer && + /// ) + /// ``` + /// + /// because the `Long` has two leading new lines after removing parentheses, the one after `a` and the one after the opening `(`. + /// + /// However, it is important to leave at least one leading new line in front of the token's leading trivia if there's a comment in the leading trivia because + /// because we want that leading comments that are preceded by a line break to be formatted on their own line. + /// + /// ```javascript + /// ( + /// // comment + /// a + /// ) + /// ``` + /// + /// Keep the line break before the `// comment` or the formatter will format the comment on the same line as the `(` token + /// + /// ```javascript + /// ( // comment + /// a + /// ) + /// ``` + /// + /// Which may turn `//comment` into a trailing comment that then gets formatted differently on the next formatting pass, resulting in instability issues. + fn visit_parenthesized( + &mut self, + parenthesized: JsAnyParenthesized, + ) -> VisitNodeSignal { + let (l_paren, inner, r_paren) = match ( + parenthesized.l_paren_token(), + parenthesized.inner(), + parenthesized.r_paren_token(), + ) { + (Ok(l_paren), Ok(inner), Ok(r_paren)) => { + let prev_token = l_paren.prev_token(); + + // Keep parentheses around unknown expressions. Rome can't know the precedence. + if inner.kind().is_unknown() + // Don't remove parentheses if they have skipped trivia. We don't know for certain what the intended syntax is. + // Nor if there's a leading type cast comment + || has_type_cast_comment_or_skipped(&l_paren.leading_trivia()) + || prev_token.map_or(false, |prev_token| has_type_cast_comment_or_skipped(&prev_token.trailing_trivia())) + || r_paren.leading_trivia().has_skipped() + { + return VisitNodeSignal::Traverse(parenthesized.into_syntax()); + } else { + (l_paren, inner, r_paren) + } + } + _ => { + // At least one missing child, handle as a regular node + return VisitNodeSignal::Traverse(parenthesized.into_syntax()); + } + }; + + let inner_trimmed_range = inner.text_trimmed_range(); + // Store away the inner offset because the new returned inner might be a detached node + let mut inner_offset = inner.text_range().start(); + let inner = self.transform(inner); + + match inner.first_token() { + // This can only happen if we have `()` which is highly unlikely to ever be the case. + // Return the parenthesized expression as is. This will be formatted as verbatim + None => { + let updated = match parenthesized { + JsAnyParenthesized::JsParenthesizedExpression(expression) => { + // SAFETY: Safe because the rewriter never rewrites an expression to a non expression. + expression + .with_expression(JsAnyExpression::unwrap_cast(inner)) + .into_syntax() + } + JsAnyParenthesized::JsParenthesizedAssignment(assignment) => { + // SAFETY: Safe because the rewriter never rewrites an assignment to a non assignment. + assignment + .with_assignment(JsAnyAssignment::unwrap_cast(inner)) + .into_syntax() + } + }; + + VisitNodeSignal::Replace(updated) + } + + Some(first_token) => { + self.source_map.extend_trimmed_node_range( + inner_trimmed_range, + parenthesized.syntax().text_trimmed_range(), + ); + + self.source_map + .add_deleted_range(l_paren.text_trimmed_range()); + + let l_paren_trivia = chain_pieces( + l_paren.leading_trivia().pieces(), + l_paren.trailing_trivia().pieces(), + ); + + let mut leading_trivia = first_token.leading_trivia().pieces().peekable(); + let mut first_new_line = None; + + // The leading whitespace before the opening parens replaces the whitespace before the node. + while let Some(trivia) = leading_trivia.peek() { + if trivia.is_newline() && first_new_line.is_none() { + inner_offset += trivia.text_len(); + first_new_line = Some((inner_offset, leading_trivia.next().unwrap())); + } else if trivia.is_whitespace() || trivia.is_newline() { + let trivia_len = trivia.text_len(); + self.source_map + .add_deleted_range(TextRange::at(inner_offset, trivia_len)); + inner_offset += trivia_len; + leading_trivia.next(); + } else { + break; + } + } + + // Remove all leading new lines directly in front of the token but keep the leading new-line if it precedes a skipped token trivia or a comment. + if leading_trivia.peek().is_none() && first_new_line.is_some() { + let (inner_offset, new_line) = first_new_line.take().unwrap(); + + self.source_map + .add_deleted_range(TextRange::at(inner_offset, new_line.text_len())); + } + + let leading_trivia = chain_pieces( + first_new_line.map(|(_, trivia)| trivia).into_iter(), + leading_trivia, + ); + + let new_leading = chain_pieces(l_paren_trivia, leading_trivia); + let new_first = first_token.with_leading_trivia_pieces(new_leading); + + // SAFETY: Calling `unwrap` is safe because we know that `inner_first` is part of the `inner` subtree. + let updated = inner + .replace_child(first_token.into(), new_first.into()) + .unwrap(); + + let r_paren_trivia = chain_pieces( + r_paren.leading_trivia().pieces(), + r_paren.trailing_trivia().pieces(), + ); + + // SAFETY: Calling `unwrap` is safe because `last_token` only returns `None` if a node's subtree + // doesn't contain ANY token, but we know that the subtree contains at least the first token. + let last_token = updated.last_token().unwrap(); + + let new_last = last_token.with_trailing_trivia_pieces(chain_pieces( + last_token.trailing_trivia().pieces(), + r_paren_trivia, + )); + + self.source_map + .add_deleted_range(r_paren.text_trimmed_range()); + + // SAFETY: Calling `unwrap` is safe because we know that `last_token` is part of the `updated` subtree. + VisitNodeSignal::Replace( + updated + .replace_child(last_token.into(), new_last.into()) + .unwrap(), + ) + } + } + } + + /// Re-balances right-recursive logical expressions with the same operator to be left recursive (relies on the parentheses removal) + /// + /// ```javascript + /// a && (b && c) + /// ``` + /// + /// has the tree (parentheses omitted) + /// + /// ```text + /// && + /// a && + /// b c + /// ``` + /// + /// This transform re-balances the tree so that it becomes left-recursive + /// + /// ```text + /// && + /// && c + /// a b + /// ``` + /// + /// This is required so that the binary like expression formatting only has to resolve left recursive expressions. + fn visit_logical_expression( + &mut self, + logical: JsLogicalExpression, + ) -> VisitNodeSignal { + match (logical.left(), logical.operator_token(), logical.right()) { + (Ok(left), Ok(operator), Ok(right)) => { + let left_key = left.syntax().key(); + let operator_key = operator.key(); + let right_key = right.syntax().key(); + + // SAFETY: Safe because the rewriter never rewrites an expression to a non expression. + let left = JsAnyExpression::unwrap_cast(self.transform(left.into_syntax())); + let operator = self.visit_token(operator); + // SAFETY: Safe because the rewriter never rewrites an expression to a non expression. + let right = JsAnyExpression::unwrap_cast(self.transform(right.into_syntax())); + + let updated = match right { + JsAnyExpression::JsLogicalExpression(right_logical) => { + match ( + right_logical.left(), + right_logical.operator_token(), + right_logical.right(), + ) { + (Ok(right_left), Ok(right_operator), Ok(right_right)) + if right_operator.kind() == operator.kind() => + { + logical + .with_left( + rome_js_factory::make::js_logical_expression( + left, operator, right_left, + ) + .into(), + ) + .with_operator_token_token(right_operator) + .with_right(right_right) + } + + // Don't re-balance a logical expression that has syntax errors + _ => logical + .with_left(left) + .with_operator_token_token(operator) + .with_right(right_logical.into()), + } + } + + // Don't re-balance logical expressions with different operators + // Avoid updating the node if none of the children have changed to avoid + // re-spinning all parents. + right => { + if left.syntax().key() != left_key + || operator.key() != operator_key + || right.syntax().key() != right_key + { + logical + .with_left(left) + .with_operator_token_token(operator) + .with_right(right) + } else { + logical + } + } + }; + + VisitNodeSignal::Replace(updated.into_syntax()) + } + _ => VisitNodeSignal::Traverse(logical.into_syntax()), + } + } + + pub(crate) fn finish(self) -> TransformSourceMap { + self.source_map.finish() + } +} + +impl SyntaxRewriter for JsFormatSyntaxRewriter { + type Language = JsLanguage; + + fn visit_node(&mut self, node: JsSyntaxNode) -> VisitNodeSignal { + match node.kind() { + kind if JsAnyParenthesized::can_cast(kind) => { + let parenthesized = JsAnyParenthesized::unwrap_cast(node); + + self.visit_parenthesized(parenthesized) + } + JsSyntaxKind::JS_LOGICAL_EXPRESSION => { + let logical = JsLogicalExpression::unwrap_cast(node); + + self.visit_logical_expression(logical) + } + _ => VisitNodeSignal::Traverse(node), + } + } +} + +fn has_type_cast_comment_or_skipped(trivia: &SyntaxTrivia) -> bool { + trivia.pieces().any(|piece| { + if let Some(comment) = piece.as_comments() { + is_type_comment(&comment) + } else { + piece.is_skipped() + } + }) +} + +/// Returns `true` if `comment` is a [Closure type comment](https://github.com/google/closure-compiler/wiki/Types-in-the-Closure-Type-System) +/// or [TypeScript type comment](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type) +fn is_type_comment(comment: &SyntaxTriviaPieceComments) -> bool { + let text = comment.text(); + + // Must be a `/**` comment + if !text.starts_with("/**") { + return false; + } + + text.trim_start_matches("/**") + .trim_end_matches("*/") + .split_whitespace() + .any(|word| match word.strip_prefix("@type") { + Some(after) => after.is_empty() || after.starts_with('{'), + None => false, + }) +} + +fn chain_pieces(first: F, second: S) -> ChainTriviaPiecesIterator +where + F: Iterator>, + S: Iterator>, +{ + ChainTriviaPiecesIterator::new(first, second) +} + +/// Chain iterator that chains two iterators over syntax trivia together. +/// +/// This is the same as Rust's [Chain] iterator but implements [ExactSizeIterator]. +/// Rust doesn't implement [ExactSizeIterator] because adding the sizes of both pieces may overflow. +/// +/// Implementing [ExactSizeIterator] in our case is safe because this may only overflow if +/// a source document has more than 2^32 trivia which isn't possible because our source documents are limited to 2^32 +/// length. +struct ChainTriviaPiecesIterator { + first: Option, + second: S, +} + +impl ChainTriviaPiecesIterator { + fn new(first: F, second: S) -> Self { + Self { + first: Some(first), + second, + } + } +} + +impl Iterator for ChainTriviaPiecesIterator +where + F: Iterator>, + S: Iterator>, +{ + type Item = SyntaxTriviaPiece; + + fn next(&mut self) -> Option { + match &mut self.first { + Some(first) => match first.next() { + Some(next) => Some(next), + None => { + self.first.take(); + self.second.next() + } + }, + None => self.second.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match &self.first { + Some(first) => { + let (first_lower, first_upper) = first.size_hint(); + let (second_lower, second_upper) = self.second.size_hint(); + + let lower = first_lower.saturating_add(second_lower); + + let upper = match (first_upper, second_upper) { + (Some(first), Some(second)) => first.checked_add(second), + _ => None, + }; + + (lower, upper) + } + None => self.second.size_hint(), + } + } +} + +impl FusedIterator for ChainTriviaPiecesIterator +where + F: Iterator>, + S: Iterator>, +{ +} + +impl ExactSizeIterator for ChainTriviaPiecesIterator +where + F: ExactSizeIterator>, + S: ExactSizeIterator>, +{ + fn len(&self) -> usize { + match &self.first { + Some(first) => { + let first_len = first.len(); + let second_len = self.second.len(); + + // SAFETY: Should be safe because a program can never contain more than u32 pieces + // because the text ranges are represented as u32 (and each piece must at least contain a single character). + first_len + second_len + } + None => self.second.len(), + } + } +} + +#[cfg(test)] +mod tests { + use super::JsFormatSyntaxRewriter; + use crate::{format_node, JsFormatOptions}; + use rome_formatter::{SourceMarker, TransformSourceMap}; + use rome_js_parser::parse_module; + use rome_js_syntax::{ + JsArrayExpression, JsBinaryExpression, JsExpressionStatement, JsIdentifierExpression, + JsLogicalExpression, JsSequenceExpression, JsStringLiteralExpression, JsSyntaxNode, + }; + use rome_rowan::{AstNode, SyntaxRewriter, TextSize}; + + #[test] + fn rebalances_logical_expressions() { + let root = parse_module("a && (b && c)", 0).syntax(); + + let transformed = JsFormatSyntaxRewriter::new(&root).transform(root.clone()); + + // Changed the root tree + assert_ne!(&transformed, &root); + + // Removes parentheses + assert_eq!(&transformed.text().to_string(), "a && b && c"); + + let mut logical_expressions: Vec<_> = transformed + .descendants() + .filter_map(JsLogicalExpression::cast) + .collect(); + + assert_eq!(logical_expressions.len(), 2); + + let left = logical_expressions.pop().unwrap(); + let top = logical_expressions.pop().unwrap(); + + assert_eq!(top.left().unwrap().syntax(), left.syntax()); + assert_eq!(&top.right().unwrap().text(), "c"); + + assert_eq!(left.left().unwrap().text(), "a"); + assert_eq!(left.right().unwrap().text(), "b"); + } + + #[test] + fn only_rebalances_logical_expressions_with_same_operator() { + let root = parse_module("a && (b || c)", 0).syntax(); + let transformed = JsFormatSyntaxRewriter::new(&root).transform(root.clone()); + + // Removes parentheses + assert_eq!(&transformed.text().to_string(), "a && b || c"); + + let logical_expressions: Vec<_> = transformed + .descendants() + .filter_map(JsLogicalExpression::cast) + .collect(); + + assert_eq!(logical_expressions.len(), 2); + + let top = logical_expressions.first().unwrap(); + let right = logical_expressions.last().unwrap(); + + assert_eq!(top.left().unwrap().text(), "a"); + assert_eq!(top.right().unwrap().syntax(), right.syntax()); + assert_eq!(right.left().unwrap().text(), "b"); + assert_eq!(right.right().unwrap().text(), "c"); + } + + #[test] + fn single_parentheses() { + let (transformed, source_map) = source_map_test("(a)"); + + assert_eq!(&transformed.text(), "a"); + + let identifier = transformed + .descendants() + .find_map(JsIdentifierExpression::cast) + .unwrap(); + + assert_eq!(source_map.trimmed_source_text(identifier.syntax()), "(a)"); + } + + #[test] + fn nested_parentheses() { + let (transformed, source_map) = source_map_test("((a))"); + + assert_eq!(&transformed.text(), "a"); + + let identifier = transformed + .descendants() + .find_map(JsIdentifierExpression::cast) + .unwrap(); + + assert_eq!(source_map.trimmed_source_text(identifier.syntax()), "((a))"); + } + + #[test] + fn test_logical_expression_source_map() { + let (transformed, source_map) = source_map_test("(a && (b && c))"); + + let logical_expressions: Vec<_> = transformed + .descendants() + .filter_map(JsLogicalExpression::cast) + .collect(); + + assert_eq!(2, logical_expressions.len()); + + assert_eq!( + source_map.trimmed_source_text(logical_expressions[0].syntax()), + "(a && (b && c))" + ); + + assert_eq!( + source_map.trimmed_source_text(logical_expressions[1].syntax()), + "a && (b" + ); + } + + #[test] + fn adjacent_nodes() { + let (transformed, source_map) = source_map_test("(a + b)"); + + assert_eq!(&transformed.text(), "a + b"); + + let identifiers: Vec<_> = transformed + .descendants() + .filter_map(JsIdentifierExpression::cast) + .collect(); + + assert_eq!(2, identifiers.len()); + // Parentheses should be associated with the binary expression + assert_eq!(source_map.trimmed_source_text(identifiers[0].syntax()), "a"); + assert_eq!(source_map.trimmed_source_text(identifiers[1].syntax()), "b"); + + let binary = transformed + .descendants() + .find_map(JsBinaryExpression::cast) + .unwrap(); + assert_eq!(source_map.trimmed_source_text(binary.syntax()), "(a + b)") + } + + #[test] + fn intersecting_ranges() { + let (transformed, source_map) = source_map_test("(interface, \"foo\");"); + + assert_eq!(&transformed.text(), "interface, \"foo\";"); + + let string_literal = transformed + .descendants() + .find_map(JsStringLiteralExpression::cast) + .unwrap(); + + assert_eq!( + source_map.trimmed_source_text(string_literal.syntax()), + "\"foo\"" + ); + + let sequence = transformed + .descendants() + .find_map(JsSequenceExpression::cast) + .unwrap(); + assert_eq!( + source_map.trimmed_source_text(sequence.syntax()), + "(interface, \"foo\")" + ); + } + + #[test] + fn deep() { + let src = r#"[ + (2*n)/(r-l), 0, (r+l)/(r-l), 0, + 0, (2*n)/(t-b), (t+b)/(t-b), 0, + 0, 0, -(f+n)/(f-n), -(2*f*n)/(f-n), + 0, 0, -1, 0, +];"#; + + let (transformed, source_map) = source_map_test(src); + + let array = transformed + .descendants() + .find_map(JsArrayExpression::cast) + .unwrap(); + + assert_eq!( + source_map.trimmed_source_text(array.syntax()), + r#"[ + (2*n)/(r-l), 0, (r+l)/(r-l), 0, + 0, (2*n)/(t-b), (t+b)/(t-b), 0, + 0, 0, -(f+n)/(f-n), -(2*f*n)/(f-n), + 0, 0, -1, 0, +]"# + ); + } + + #[test] + fn enclosing_node() { + let (transformed, source_map) = source_map_test("(a + b);"); + + assert_eq!(&transformed.text(), "a + b;"); + + let expression_statement = transformed + .descendants() + .find_map(JsExpressionStatement::cast) + .unwrap(); + + assert_eq!( + source_map.trimmed_source_text(expression_statement.syntax()), + "(a + b);" + ); + } + + #[test] + fn trailing_whitespace() { + let (transformed, source_map) = source_map_test("(a + b );"); + + assert_eq!(&transformed.text(), "a + b ;"); + + let binary = transformed + .descendants() + .find_map(JsBinaryExpression::cast) + .unwrap(); + + assert_eq!( + source_map.trimmed_source_text(binary.syntax()), + "(a + b )" + ); + } + + #[test] + fn first_token_leading_whitespace() { + let (transformed, _) = source_map_test("a;\n(\n a + b);"); + + // Trims the leading whitespace in front of the expression's first token. + assert_eq!(&transformed.text(), "a;\na + b;"); + } + + #[test] + fn first_token_leading_whitespace_before_comment() { + let (transformed, _) = source_map_test("a;(\n\n/* comment */\n a + b);"); + + // Keeps at least one new line before a leading comment. + assert_eq!(&transformed.text(), "a;\n/* comment */\n a + b;"); + } + + #[test] + fn comments() { + let (transformed, source_map) = + source_map_test("/* outer */ (/* left */ a + b /* right */) /* outer end */;"); + + assert_eq!( + &transformed.text(), + "/* outer */ /* left */ a + b /* right */ /* outer end */;" + ); + + let binary = transformed + .descendants() + .find_map(JsBinaryExpression::cast) + .unwrap(); + + assert_eq!( + source_map.trimmed_source_text(binary.syntax()), + "(/* left */ a + b /* right */)" + ); + } + + fn source_map_test(input: &str) -> (JsSyntaxNode, TransformSourceMap) { + let tree = parse_module(input, 0).syntax(); + + let mut rewriter = JsFormatSyntaxRewriter::new(&tree); + let transformed = rewriter.transform(tree); + let source_map = rewriter.finish(); + + (transformed, source_map) + } + + #[test] + fn mappings() { + let (transformed, source_map) = source_map_test("(((a * b) * c)) / 3"); + + let formatted = format_node(JsFormatOptions::default(), &transformed).unwrap(); + let printed = formatted.print(); + + assert_eq!(printed.as_code(), "(a * b * c) / 3;\n"); + + let mapped = source_map.map_printed(printed); + let markers = mapped.into_sourcemap(); + + assert_eq!( + markers, + vec![ + // `(` + SourceMarker { + source: TextSize::from(3), + dest: TextSize::from(0) + }, + // `a` + SourceMarker { + source: TextSize::from(3), + dest: TextSize::from(1) + }, + // ` ` + SourceMarker { + source: TextSize::from(4), + dest: TextSize::from(2) + }, + // `*` + SourceMarker { + source: TextSize::from(5), + dest: TextSize::from(3) + }, + // ` ` + SourceMarker { + source: TextSize::from(6), + dest: TextSize::from(4) + }, + // `b` + SourceMarker { + source: TextSize::from(7), + dest: TextSize::from(5) + }, + // ` ` + SourceMarker { + source: TextSize::from(9), + dest: TextSize::from(6) + }, + // `*` + SourceMarker { + source: TextSize::from(10), + dest: TextSize::from(7) + }, + // ` ` + SourceMarker { + source: TextSize::from(11), + dest: TextSize::from(8) + }, + // `c` + SourceMarker { + source: TextSize::from(12), + dest: TextSize::from(9) + }, + // `)` + SourceMarker { + source: TextSize::from(15), + dest: TextSize::from(10), + }, + // ` ` + SourceMarker { + source: TextSize::from(15), + dest: TextSize::from(11), + }, + // `/` + SourceMarker { + source: TextSize::from(16), + dest: TextSize::from(12), + }, + // ` ` + SourceMarker { + source: TextSize::from(17), + dest: TextSize::from(13), + }, + // `3` + SourceMarker { + source: TextSize::from(18), + dest: TextSize::from(14), + }, + // `;` + SourceMarker { + source: TextSize::from(19), + dest: TextSize::from(15), + }, + // `\n` + SourceMarker { + source: TextSize::from(19), + dest: TextSize::from(16), + }, + ] + ); + } +} diff --git a/crates/rome_js_formatter/src/ts/assignments/as_assignment.rs b/crates/rome_js_formatter/src/ts/assignments/as_assignment.rs index 8df765b4722..3dad08bb83f 100644 --- a/crates/rome_js_formatter/src/ts/assignments/as_assignment.rs +++ b/crates/rome_js_formatter/src/ts/assignments/as_assignment.rs @@ -1,8 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyAssignment, JsAnyAssignmentPattern, TsAsAssignment}; +use rome_js_syntax::TsAsAssignment; use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, TsAsAssignmentFields}; #[derive(Debug, Clone, Default)] @@ -33,18 +33,6 @@ impl FormatNodeRule for FormatTsAsAssignment { } } -impl AssignmentNode for TsAsAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for TsAsAssignment { fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { matches!( diff --git a/crates/rome_js_formatter/src/ts/assignments/non_null_assertion_assignment.rs b/crates/rome_js_formatter/src/ts/assignments/non_null_assertion_assignment.rs index da025d23cc0..8988da914d1 100644 --- a/crates/rome_js_formatter/src/ts/assignments/non_null_assertion_assignment.rs +++ b/crates/rome_js_formatter/src/ts/assignments/non_null_assertion_assignment.rs @@ -1,11 +1,9 @@ use crate::prelude::*; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; use rome_js_syntax::TsNonNullAssertionAssignmentFields; -use rome_js_syntax::{ - JsAnyAssignment, JsAnyAssignmentPattern, JsSyntaxNode, TsNonNullAssertionAssignment, -}; +use rome_js_syntax::{JsSyntaxNode, TsNonNullAssertionAssignment}; #[derive(Debug, Clone, Default)] pub struct FormatTsNonNullAssertionAssignment; @@ -28,18 +26,6 @@ impl FormatNodeRule for FormatTsNonNullAssertionAs } } -impl AssignmentNode for TsNonNullAssertionAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for TsNonNullAssertionAssignment { #[inline] fn needs_parentheses(&self) -> bool { diff --git a/crates/rome_js_formatter/src/ts/assignments/type_assertion_assignment.rs b/crates/rome_js_formatter/src/ts/assignments/type_assertion_assignment.rs index 4b64afe4904..4187c061d4b 100644 --- a/crates/rome_js_formatter/src/ts/assignments/type_assertion_assignment.rs +++ b/crates/rome_js_formatter/src/ts/assignments/type_assertion_assignment.rs @@ -1,11 +1,8 @@ use crate::prelude::*; use rome_formatter::write; -use rome_js_syntax::{ - JsAnyAssignment, JsAnyAssignmentPattern, JsSyntaxKind, JsSyntaxNode, - TsTypeAssertionAssignmentFields, -}; +use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, TsTypeAssertionAssignmentFields}; -use crate::parentheses::{AssignmentNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_js_syntax::TsTypeAssertionAssignment; #[derive(Debug, Clone, Default)] @@ -39,18 +36,6 @@ impl FormatNodeRule for FormatTsTypeAssertionAssignme } } -impl AssignmentNode for TsTypeAssertionAssignment { - #[inline] - fn resolve(&self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self.clone())) - } - - #[inline] - fn into_resolved(self) -> JsAnyAssignmentPattern { - JsAnyAssignmentPattern::JsAnyAssignment(JsAnyAssignment::from(self)) - } -} - impl NeedsParentheses for TsTypeAssertionAssignment { fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { matches!( diff --git a/crates/rome_js_formatter/src/ts/expressions/as_expression.rs b/crates/rome_js_formatter/src/ts/expressions/as_expression.rs index ceea654aacf..d0acf502d0c 100644 --- a/crates/rome_js_formatter/src/ts/expressions/as_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/as_expression.rs @@ -1,11 +1,11 @@ use crate::prelude::*; use crate::parentheses::{ - is_binary_like_left_or_right, is_callee, is_member_object, ExpressionNode, NeedsParentheses, + is_binary_like_left_or_right, is_callee, is_member_object, NeedsParentheses, }; use crate::ts::expressions::type_assertion_expression::type_cast_like_needs_parens; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, TsAsExpressionFields}; +use rome_js_syntax::TsAsExpressionFields; use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, TsAsExpression}; #[derive(Debug, Clone, Default)] @@ -32,7 +32,7 @@ impl FormatNodeRule for FormatTsAsExpression { ] }); - let parent = node.resolve_parent(); + let parent = node.syntax().parent(); let is_callee_or_object = parent.map_or(false, |parent| { is_callee(node.syntax(), &parent) || is_member_object(node.syntax(), &parent) @@ -63,18 +63,6 @@ impl NeedsParentheses for TsAsExpression { } } -impl ExpressionNode for TsAsExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - #[cfg(test)] mod tests { diff --git a/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs b/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs index 5dc096bfc9c..7ada1b0bb9f 100644 --- a/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs @@ -1,9 +1,9 @@ use crate::prelude::*; use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsSyntaxKind, TsNonNullAssertionExpressionFields}; +use rome_js_syntax::{JsSyntaxKind, TsNonNullAssertionExpressionFields}; use rome_js_syntax::{JsSyntaxNode, TsNonNullAssertionExpression}; #[derive(Debug, Clone, Default)] @@ -34,15 +34,3 @@ impl NeedsParentheses for TsNonNullAssertionExpression { || member_chain_callee_needs_parens(self.clone().into(), parent) } } - -impl ExpressionNode for TsNonNullAssertionExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} 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 f8a360cae2e..7e81376a0fc 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,10 +1,8 @@ use crate::prelude::*; -use crate::parentheses::{ - is_callee, is_member_object, is_spread, is_tag, ExpressionNode, NeedsParentheses, -}; +use crate::parentheses::{is_callee, is_member_object, is_spread, is_tag, NeedsParentheses}; use rome_formatter::write; -use rome_js_syntax::{JsAnyExpression, JsSyntaxNode}; +use rome_js_syntax::JsSyntaxNode; use rome_js_syntax::{JsSyntaxKind, TsTypeAssertionExpression, TsTypeAssertionExpressionFields}; #[derive(Debug, Clone, Default)] @@ -47,18 +45,6 @@ impl NeedsParentheses for TsTypeAssertionExpression { } } -impl ExpressionNode for TsTypeAssertionExpression { - #[inline] - fn resolve(&self) -> JsAnyExpression { - self.clone().into() - } - - #[inline] - fn into_resolved(self) -> JsAnyExpression { - self.into() - } -} - pub(super) fn type_cast_like_needs_parens(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { debug_assert!(matches!( node.kind(), diff --git a/crates/rome_js_formatter/src/utils/assignment_like.rs b/crates/rome_js_formatter/src/utils/assignment_like.rs index 4ac629e604f..aab1fb48184 100644 --- a/crates/rome_js_formatter/src/utils/assignment_like.rs +++ b/crates/rome_js_formatter/src/utils/assignment_like.rs @@ -1,11 +1,9 @@ -use crate::parentheses::{ - get_expression_left_side, resolve_parent, ExpressionNode, NeedsParentheses, -}; +use crate::parentheses::get_expression_left_side; use crate::prelude::*; use crate::utils::member_chain::is_member_call_chain; use crate::utils::object::write_member_name; use crate::utils::{JsAnyBinaryLikeExpression, JsAnyBinaryLikeLeftExpression}; -use rome_formatter::{format_args, write, CstFormatContext, VecBuffer}; +use rome_formatter::{format_args, write, CstFormatContext, FormatOptions, VecBuffer}; use rome_js_syntax::{ JsAnyAssignmentPattern, JsAnyBindingPattern, JsAnyCallArgument, JsAnyClassMemberName, JsAnyExpression, JsAnyFunctionBody, JsAnyObjectAssignmentPatternMember, @@ -588,7 +586,7 @@ impl JsAnyAssignmentLike { return Ok(AssignmentLikeLayout::SuppressedInitializer); } } - let right_expression = right.as_expression().map(ExpressionNode::into_resolved); + let right_expression = right.as_expression(); if let Some(layout) = self.chain_formatting_layout(right_expression.as_ref())? { return Ok(layout); @@ -621,9 +619,6 @@ impl JsAnyAssignmentLike { let right_expression = iter::successors(right_expression, |expression| match expression { JsAnyExpression::JsUnaryExpression(unary) => unary.argument().ok(), JsAnyExpression::TsNonNullAssertionExpression(assertion) => assertion.expression().ok(), - JsAnyExpression::JsParenthesizedExpression(parenthesized) => { - parenthesized.expression().ok() - } _ => None, }) .last(); @@ -693,14 +688,14 @@ impl JsAnyAssignmentLike { let upper_chain_is_eligible = // First, we check if the current node is an assignment expression if let JsAnyAssignmentLike::JsAssignmentExpression(assignment) = self { - assignment.resolve_parent().map_or(false, |parent| { + assignment.syntax().parent().map_or(false, |parent| { // Then we check if the parent is assignment expression or variable declarator if matches!( parent.kind(), JsSyntaxKind::JS_ASSIGNMENT_EXPRESSION | JsSyntaxKind::JS_INITIALIZER_CLAUSE ) { - let great_parent_kind = resolve_parent(&parent).map(|n| n.kind()); + let great_parent_kind = parent.parent().map(|n| n.kind()); // Finally, we check the great parent. // The great parent triggers the eligibility when // - the current node that we were inspecting is not a "tail" @@ -731,7 +726,7 @@ impl JsAnyAssignmentLike { match this_body { JsAnyFunctionBody::JsAnyExpression(expression) => { if matches!( - expression.resolve(), + expression, JsAnyExpression::JsArrowFunctionExpression(_) ) { Some(AssignmentLikeLayout::ChainTailArrowFunction) @@ -835,13 +830,11 @@ pub(crate) fn should_break_after_operator(right: &JsAnyExpression) -> SyntaxResu break; } - let right = right.resolve(); - let result = match right { // head is a long chain, meaning that right -> right are both assignment expressions JsAnyExpression::JsAssignmentExpression(assignment) => { matches!( - assignment.right()?.resolve(), + assignment.right()?, JsAnyExpression::JsAssignmentExpression(_) ) } @@ -854,7 +847,7 @@ pub(crate) fn should_break_after_operator(right: &JsAnyExpression) -> SyntaxResu JsAnyExpression::JsSequenceExpression(_) => true, JsAnyExpression::JsConditionalExpression(conditional) => { - JsAnyBinaryLikeExpression::cast(conditional.test()?.into_resolved_syntax()) + JsAnyBinaryLikeExpression::cast(conditional.test()?.into_syntax()) .map_or(false, |expression| { !expression.should_inline_logical_expression() }) @@ -1063,7 +1056,6 @@ fn is_poorly_breakable_member_or_call_chain( is_chain = true; Some(node.object()?) } - JsAnyExpression::JsParenthesizedExpression(node) => Some(node.expression()?), JsAnyExpression::JsIdentifierExpression(_) | JsAnyExpression::JsThisExpression(_) => { is_chain_head_simple = true; break; @@ -1121,7 +1113,7 @@ fn is_short_argument(argument: JsAnyCallArgument, threshold: u16) -> SyntaxResul } if let JsAnyCallArgument::JsAnyExpression(expression) = argument { - let is_short_argument = match expression.resolve() { + let is_short_argument = match expression { JsAnyExpression::JsThisExpression(_) => true, JsAnyExpression::JsIdentifierExpression(identifier) => { identifier.name()?.value_token()?.text_trimmed().len() <= threshold as usize 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 ac03c6c727c..a732fa15dd5 100644 --- a/crates/rome_js_formatter/src/utils/binary_like_expression.rs +++ b/crates/rome_js_formatter/src/utils/binary_like_expression.rs @@ -58,13 +58,12 @@ use rome_formatter::{format_args, write, Buffer, CommentStyle, CstFormatContext} use rome_js_syntax::{ JsAnyExpression, JsAnyInProperty, JsBinaryExpression, JsBinaryOperator, JsDoWhileStatement, JsIfStatement, JsInExpression, JsInstanceofExpression, JsLogicalExpression, JsLogicalOperator, - JsParenthesizedExpression, JsPrivateName, JsSwitchStatement, JsSyntaxKind, JsSyntaxNode, - JsSyntaxToken, JsUnaryExpression, JsWhileStatement, OperatorPrecedence, + JsPrivateName, JsSwitchStatement, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, JsUnaryExpression, + JsWhileStatement, OperatorPrecedence, }; use crate::parentheses::{ - is_arrow_function_body, is_callee, is_member_object, is_spread, is_tag, resolve_parent, - ExpressionNode, NeedsParentheses, + is_arrow_function_body, is_callee, is_member_object, is_spread, is_tag, NeedsParentheses, }; use crate::context::JsCommentStyle; @@ -81,7 +80,7 @@ declare_node_union! { impl Format for JsAnyBinaryLikeExpression { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let parent = self.resolve_parent(); + let parent = self.syntax().parent(); let is_inside_condition = self.is_inside_condition(parent.as_ref()); let parts = split_into_left_and_right_sides(self, is_inside_condition)?; @@ -193,14 +192,10 @@ fn split_into_left_and_right_sides( // Calling skip_subtree prevents the exit event being triggered for this event. expressions.skip_subtree(); - items.push(BinaryLeftOrRightSide::Left { - is_root: binary.syntax() == root.syntax(), - parent: binary, - }); + items.push(BinaryLeftOrRightSide::Left { parent: binary }); } } VisitEvent::Exit(expression) => items.push(BinaryLeftOrRightSide::Right { - is_root: expression.syntax() == root.syntax(), parent: expression, inside_condition, }), @@ -248,14 +243,12 @@ fn should_indent_if_parent_inlines(parent: Option<&JsSyntaxNode>) -> bool { parent.map_or(false, |parent| match parent.kind() { JsSyntaxKind::JS_ASSIGNMENT_EXPRESSION | JsSyntaxKind::JS_PROPERTY_OBJECT_MEMBER => true, - JsSyntaxKind::JS_INITIALIZER_CLAUSE => { - resolve_parent(parent).map_or(false, |grand_parent| { - matches!( - grand_parent.kind(), - JsSyntaxKind::JS_VARIABLE_DECLARATOR | JsSyntaxKind::JS_PROPERTY_CLASS_MEMBER - ) - }) - } + JsSyntaxKind::JS_INITIALIZER_CLAUSE => parent.parent().map_or(false, |grand_parent| { + matches!( + grand_parent.kind(), + JsSyntaxKind::JS_VARIABLE_DECLARATOR | JsSyntaxKind::JS_PROPERTY_CLASS_MEMBER + ) + }), _ => false, }) } @@ -266,10 +259,7 @@ enum BinaryLeftOrRightSide { /// A terminal left hand side of a binary expression. /// /// Formats the left hand side only. - Left { - parent: JsAnyBinaryLikeExpression, - is_root: bool, - }, + Left { parent: JsAnyBinaryLikeExpression }, /// The right hand side of a binary expression. /// Formats the operand together with the right hand side. @@ -277,7 +267,6 @@ enum BinaryLeftOrRightSide { parent: JsAnyBinaryLikeExpression, /// Is the parent the condition of a `if` / `while` / `do-while` / `for` statement? inside_condition: bool, - is_root: bool, }, } @@ -285,17 +274,14 @@ impl BinaryLeftOrRightSide { #[allow(unused)] fn is_jsx(&self) -> bool { match self { - BinaryLeftOrRightSide::Left { parent, .. } => match parent.left() { - Ok(JsAnyBinaryLikeLeftExpression::JsAnyExpression(expression)) => { - matches!(expression.resolve(), JsAnyExpression::JsxTagExpression(_)) - } - _ => false, - }, + BinaryLeftOrRightSide::Left { parent, .. } => matches!( + parent.left(), + Ok(JsAnyBinaryLikeLeftExpression::JsAnyExpression( + JsAnyExpression::JsxTagExpression(_), + )) + ), BinaryLeftOrRightSide::Right { parent, .. } => { - matches!( - parent.right().map(JsAnyExpression::into_resolved), - Ok(JsAnyExpression::JsxTagExpression(_)) - ) + matches!(parent.right(), Ok(JsAnyExpression::JsxTagExpression(_))) } } } @@ -304,29 +290,12 @@ impl BinaryLeftOrRightSide { impl Format for BinaryLeftOrRightSide { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { match self { - BinaryLeftOrRightSide::Left { parent, is_root } => { - if !is_root { - for ancestor in parent.syntax().ancestors().skip(1) { - match ancestor.kind() { - JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION => { - f.context().comments().mark_suppression_checked(&ancestor); - - let parenthesized = - JsParenthesizedExpression::unwrap_cast(ancestor); - - write!(f, [format_removed(&parenthesized.l_paren_token()?)])?; - } - _ => break, - } - } - } - + BinaryLeftOrRightSide::Left { parent } => { write!(f, [group(&parent.left())]) } BinaryLeftOrRightSide::Right { parent: binary_like_expression, inside_condition: inside_parenthesis, - is_root, } => { // It's only possible to suppress the formatting of the whole binary expression formatting OR // the formatting of the right hand side value but not of a nested binary expression. @@ -351,71 +320,31 @@ impl Format for BinaryLeftOrRightSide { write!(f, [right.format()])?; - if !is_root { - for ancestor in binary_like_expression.syntax().ancestors().skip(1) { - match ancestor.kind() { - JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION => { - let parenthesized = - JsParenthesizedExpression::unwrap_cast(ancestor); - - write!(f, [format_removed(&parenthesized.r_paren_token()?)])?; - } - _ => break, - } - } - } - Ok(()) }); let syntax = binary_like_expression.syntax(); - let parent = resolve_parent(syntax); + let parent = syntax.parent(); // Doesn't match prettier that only distinguishes between logical and binary let parent_has_same_kind = parent.as_ref().map_or(false, |parent| { is_same_binary_expression_kind(binary_like_expression, parent) }); - let left_has_same_kind = - binary_like_expression - .left()? - .into_expression() - .map_or(false, |left| { - is_same_binary_expression_kind( - binary_like_expression, - &left.resolve_syntax(), - ) - }); + let left_has_same_kind = binary_like_expression + .left()? + .into_expression() + .map_or(false, |left| { + is_same_binary_expression_kind(binary_like_expression, left.syntax()) + }); let right_has_same_kind = - is_same_binary_expression_kind(binary_like_expression, &right.resolve_syntax()); + is_same_binary_expression_kind(binary_like_expression, right.syntax()); let should_break = { if has_token_trailing_line_comment(&operator_token) { true } else { - let mut current = Some(binary_like_expression.left()?.into_syntax()); - loop { - if let Some(left) = current { - if has_leading_own_line_comment(&left) { - break true; - } - - match left.kind() { - JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION => { - let parenthesized = - JsParenthesizedExpression::unwrap_cast(left); - - current = parenthesized - .expression() - .ok() - .map(AstNode::into_syntax); - } - _ => { - break false; - } - } - } - } + has_leading_own_line_comment(binary_like_expression.left()?.syntax()) } }; @@ -529,7 +458,7 @@ impl JsAnyBinaryLikeExpression { _ => return false, }; - test.map_or(false, |test| &test.resolve_syntax() == self.syntax()) + test.map_or(false, |test| test.syntax() == self.syntax()) }) } @@ -538,7 +467,7 @@ impl JsAnyBinaryLikeExpression { fn can_flatten(&self) -> SyntaxResult { let left = self.left()?.into_expression(); - let left_expression = left.as_ref().map(|expression| expression.resolve_syntax()); + let left_expression = left.map(|expression| expression.into_syntax()); if let Some(left_binary_like) = left_expression.and_then(JsAnyBinaryLikeExpression::cast) { let operator = self.operator()?; @@ -553,14 +482,12 @@ impl JsAnyBinaryLikeExpression { pub(crate) fn should_inline_logical_expression(&self) -> bool { match self { JsAnyBinaryLikeExpression::JsLogicalExpression(logical) => { - logical - .right() - .map_or(false, |right| match right.resolve() { - JsAnyExpression::JsObjectExpression(object) => !object.members().is_empty(), - JsAnyExpression::JsArrayExpression(array) => !array.elements().is_empty(), - JsAnyExpression::JsxTagExpression(_) => true, - _ => false, - }) + logical.right().map_or(false, |right| match right { + JsAnyExpression::JsObjectExpression(object) => !object.members().is_empty(), + JsAnyExpression::JsArrayExpression(array) => !array.elements().is_empty(), + JsAnyExpression::JsxTagExpression(_) => true, + _ => false, + }) } _ => false, } @@ -584,7 +511,7 @@ impl JsAnyBinaryLikeExpression { is_arrow_function_body(self.syntax(), parent) } JsSyntaxKind::JS_CONDITIONAL_EXPRESSION => { - let grand_parent = resolve_parent(parent); + let grand_parent = parent.parent(); grand_parent.map_or(false, |grand_parent| { !matches!( @@ -663,11 +590,8 @@ pub(crate) fn needs_binary_like_parentheses( return true; } - let is_right = parent - .right() - .map(ExpressionNode::into_resolved_syntax) - .as_ref() - == Ok(node.syntax()); + let is_right = + parent.right().map(AstNode::into_syntax).as_ref() == Ok(node.syntax()); // `a ** b ** c` if is_right && parent_precedence == precedence { @@ -899,7 +823,8 @@ impl BinaryLikePreorder { // SAFETY: Calling `unwrap` here is safe because the iterator only enters (traverses into) a node // if it is a valid binary like expression and it is guaranteed to have a parent. let expression = binary - .resolve_parent() + .syntax() + .parent() .and_then(JsAnyBinaryLikeExpression::cast) .unwrap(); @@ -928,7 +853,7 @@ impl Iterator for BinaryLikePreorder { .ok() .and_then(|left| left.into_expression()) .and_then(|expression| { - JsAnyBinaryLikeExpression::cast(expression.into_resolved_syntax()) + JsAnyBinaryLikeExpression::cast(expression.into_syntax()) }); if let Some(binary) = next { @@ -940,7 +865,7 @@ impl Iterator for BinaryLikePreorder { } VisitEvent::Exit(node) => { if node.syntax() != &self.start { - self.next = node.resolve_parent().map(|parent| { + self.next = node.syntax().parent().map(|parent| { // SAFETY: Calling `unwrap` here is safe because the iterator only enters (traverses into) a node // if it is a valid binary like expression. let expression = JsAnyBinaryLikeExpression::cast(parent).unwrap(); diff --git a/crates/rome_js_formatter/src/utils/conditional.rs b/crates/rome_js_formatter/src/utils/conditional.rs index c25f24482fe..59e591bf4fa 100644 --- a/crates/rome_js_formatter/src/utils/conditional.rs +++ b/crates/rome_js_formatter/src/utils/conditional.rs @@ -1,12 +1,11 @@ use crate::prelude::*; use rome_formatter::write; -use crate::parentheses::{ExpressionNode, NeedsParentheses}; use rome_js_syntax::{ JsAnyExpression, JsAssignmentExpression, JsCallExpression, JsComputedMemberExpression, - JsConditionalExpression, JsInitializerClause, JsNewExpression, JsParenthesizedExpression, - JsReturnStatement, JsStaticMemberExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, - JsThrowStatement, JsUnaryExpression, JsYieldArgument, TsAsExpression, TsConditionalType, + JsConditionalExpression, JsInitializerClause, JsNewExpression, JsReturnStatement, + JsStaticMemberExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, JsThrowStatement, + JsUnaryExpression, JsYieldArgument, TsAsExpression, TsConditionalType, TsNonNullAssertionExpression, TsType, }; use rome_rowan::{declare_node_union, match_ast, AstNode, SyntaxResult}; @@ -33,7 +32,7 @@ impl Format for JsAnyConditional { ] )?; - let is_consequent_nested = consequent.resolve().kind() == syntax.kind(); + let is_consequent_nested = consequent.syntax().kind() == syntax.kind(); if is_consequent_nested { // Add parentheses around the consequent if it is a conditional expression and fits on the same line @@ -60,7 +59,7 @@ impl Format for JsAnyConditional { ] )?; - let is_alternate_nested = alternate.resolve().kind() == syntax.kind(); + let is_alternate_nested = alternate.syntax().kind() == syntax.kind(); // Don't "indent" a nested alternate by two spaces so that the consequent and alternates nicely align // ``` @@ -183,16 +182,6 @@ declare_node_union! { ExpressionOrType = JsAnyExpression | TsType } -impl ExpressionOrType { - /// Resolves to the first non parenthesized expression. Returns self for types. - fn resolve(&self) -> JsSyntaxNode { - match self { - ExpressionOrType::JsAnyExpression(expression) => expression.resolve_syntax(), - ExpressionOrType::TsType(ty) => ty.syntax().clone(), - } - } -} - impl Format for ExpressionOrType { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { match self { @@ -286,7 +275,7 @@ impl ConditionalLayout { impl JsAnyConditional { fn layout(&self) -> ConditionalLayout { let resolved_parent = match self { - JsAnyConditional::JsConditionalExpression(conditional) => conditional.resolve_parent(), + JsAnyConditional::JsConditionalExpression(conditional) => conditional.syntax().parent(), JsAnyConditional::TsConditionalType(ty) => ty.syntax().parent(), }; @@ -324,7 +313,6 @@ impl JsAnyConditional { JsAnyConditional::JsConditionalExpression(conditional) => conditional .test() .ok() - .map(ExpressionNode::into_resolved) .map_or(false, |resolved| resolved.syntax() == node), JsAnyConditional::TsConditionalType(conditional) => { conditional.check_type().map(AstNode::into_syntax).as_ref() == Ok(node) @@ -378,10 +366,9 @@ impl JsAnyConditional { /// Returns `true` if the passed node is the `alternate` of this conditional expression. fn is_alternate(&self, node: &JsSyntaxNode) -> bool { let alternate = match self { - JsAnyConditional::JsConditionalExpression(conditional) => conditional - .alternate() - .map(ExpressionNode::into_resolved_syntax) - .ok(), + JsAnyConditional::JsConditionalExpression(conditional) => { + conditional.alternate().map(AstNode::into_syntax).ok() + } JsAnyConditional::TsConditionalType(ts_conditional) => { ts_conditional.false_type().ok().map(AstNode::into_syntax) } @@ -430,8 +417,7 @@ impl JsAnyConditional { let ancestors = layout .parent() .into_iter() - .flat_map(|parent| parent.ancestors()) - .filter(|parent| !JsParenthesizedExpression::can_cast(parent.kind())); + .flat_map(|parent| parent.ancestors()); let mut parent = None; let mut expression = JsAnyExpression::from(conditional.clone()); @@ -444,7 +430,6 @@ impl JsAnyConditional { JsCallExpression(call_expression) => { if call_expression .callee() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { @@ -457,7 +442,6 @@ impl JsAnyConditional { JsStaticMemberExpression(member_expression) => { if member_expression .object() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { @@ -469,7 +453,6 @@ impl JsAnyConditional { JsComputedMemberExpression(member_expression) => { if member_expression .object() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { @@ -481,7 +464,6 @@ impl JsAnyConditional { TsNonNullAssertionExpression(non_null_assertion) => { if non_null_assertion .expression() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { @@ -494,11 +476,10 @@ impl JsAnyConditional { // Skip over new expressions if new_expression .callee() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { - parent = new_expression.resolve_parent(); + parent = new_expression.syntax().parent(); expression = new_expression.into(); break; } @@ -508,11 +489,10 @@ impl JsAnyConditional { TsAsExpression(as_expression) => { if as_expression .expression() - .map(ExpressionNode::into_resolved) .as_ref() == Ok(&expression) { - parent = as_expression.resolve_parent(); + parent = as_expression.syntax().parent(); expression = as_expression.into(); break; } @@ -575,7 +555,7 @@ impl JsAnyConditional { _ => None, }; - argument.map_or(false, |argument| argument.resolve() == expression) + argument.map_or(false, |argument| argument == expression) } } } diff --git a/crates/rome_js_formatter/src/utils/jsx.rs b/crates/rome_js_formatter/src/utils/jsx.rs index 5260f90316c..86e439b9e7e 100644 --- a/crates/rome_js_formatter/src/utils/jsx.rs +++ b/crates/rome_js_formatter/src/utils/jsx.rs @@ -1,5 +1,5 @@ use crate::context::QuoteStyle; -use crate::parentheses::NeedsParentheses; + use crate::prelude::*; use rome_formatter::{format_args, write}; use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, JsxAnyChild, JsxChildList, JsxTagExpression}; @@ -77,7 +77,7 @@ pub enum WrapState { pub fn get_wrap_state(node: &JsxTagExpression) -> WrapState { // We skip the first item because the first item in ancestors is the node itself, i.e. // the JSX Element in this case. - let parent = node.resolve_parent(); + let parent = node.syntax().parent(); parent.map_or(WrapState::NoWrap, |parent| match parent.kind() { JsSyntaxKind::JS_ARRAY_EXPRESSION @@ -112,16 +112,6 @@ pub fn is_jsx_inside_arrow_function_inside_call_inside_expression_child( // the JSX Element in this case. let mut ancestors = node.ancestors().skip(2).peekable(); - // This matching should work with or without parentheses around the JSX element - // therefore we ignore parenthesized expressions. - if ancestors - .peek() - .map(|a| a.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION) - .unwrap_or(false) - { - ancestors.next(); - } - let required_ancestors = [ JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION, JsSyntaxKind::JS_CALL_ARGUMENT_LIST, 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 2efa1c00cca..9b689a2cfed 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 @@ -4,139 +4,12 @@ use rome_formatter::write; use rome_js_syntax::{ JsAnyExpression, JsCallExpression, JsCallExpressionFields, JsComputedMemberExpression, JsComputedMemberExpressionFields, JsIdentifierExpression, JsImportCallExpression, - JsNewExpression, JsParenthesizedExpression, JsStaticMemberExpression, - JsStaticMemberExpressionFields, JsSyntaxNode, JsThisExpression, + JsNewExpression, JsStaticMemberExpression, JsStaticMemberExpressionFields, JsSyntaxNode, + JsThisExpression, }; use rome_rowan::{AstNode, SyntaxResult}; use std::fmt::Debug; -/// One entry in a member chain. -#[derive(Clone, Debug)] -pub(crate) enum ChainEntry { - /// A member that is parenthesized in the source document - Parenthesized { - /// The chain member - member: ChainMember, - /// The top most ancestor of the chain member that is a parenthesized expression. - /// - /// ```text - /// (a.b).c() - /// ^^^ -> member - /// ^----^ -> top_most_parentheses - /// - /// ((a.b)).c() - /// ^^^ -> member - /// ^-----^ -> top most parentheses (skips inner parentheses node) - /// ``` - top_most_parentheses: JsParenthesizedExpression, - }, - Member(ChainMember), -} - -impl ChainEntry { - /// Returns the inner member - pub fn member(&self) -> &ChainMember { - match self { - ChainEntry::Parenthesized { member, .. } => member, - ChainEntry::Member(member) => member, - } - } - - /// Returns the top most parentheses node if any - pub fn top_most_parentheses(&self) -> Option<&JsParenthesizedExpression> { - match self { - ChainEntry::Parenthesized { - top_most_parentheses, - .. - } => Some(top_most_parentheses), - ChainEntry::Member(_) => None, - } - } - - pub fn into_member(self) -> ChainMember { - match self { - ChainEntry::Parenthesized { member, .. } => member, - ChainEntry::Member(member) => member, - } - } - - pub(crate) fn has_trailing_comments(&self) -> bool { - self.nodes().any(|node| node.has_trailing_comments()) - } - - /// Returns true if the member any of it's ancestor parentheses nodes has any leading comments. - pub(crate) fn has_leading_comments(&self) -> SyntaxResult { - let has_operator_comment = match self.member() { - ChainMember::StaticMember(node) => node.operator_token()?.has_leading_comments(), - _ => false, - }; - - Ok(self.nodes().any(|node| node.has_leading_comments()) || has_operator_comment) - } - - fn nodes(&self) -> impl Iterator { - let first = match self { - ChainEntry::Parenthesized { - top_most_parentheses, - .. - } => top_most_parentheses.syntax().clone(), - ChainEntry::Member(member) => member.syntax().clone(), - }; - - let is_parenthesized = matches!(self, ChainEntry::Parenthesized { .. }); - - std::iter::successors(Some(first), move |previous| { - if is_parenthesized { - JsParenthesizedExpression::cast(previous.clone()).and_then(|parenthesized| { - parenthesized.expression().map(AstNode::into_syntax).ok() - }) - } else { - None - } - }) - } -} - -impl Format for ChainEntry { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let parentheses = self.top_most_parentheses(); - - if let Some(parentheses) = parentheses { - let mut current = parentheses.clone(); - - loop { - write!(f, [format_removed(¤t.l_paren_token()?)])?; - - match current.expression()? { - JsAnyExpression::JsParenthesizedExpression(inner) => { - current = inner; - } - _ => break, - } - } - } - - write!(f, [self.member()])?; - - if let Some(parentheses) = parentheses { - let mut current = parentheses.clone(); - - loop { - write!(f, [format_removed(¤t.r_paren_token()?)])?; - - match current.expression()? { - JsAnyExpression::JsParenthesizedExpression(inner) => { - current = inner; - } - _ => break, - } - } - } - - Ok(()) - } -} - /// Data structure that holds the node with its formatted version #[derive(Clone, Debug)] pub(crate) enum ChainMember { @@ -152,6 +25,20 @@ pub(crate) enum ChainMember { } impl ChainMember { + pub(crate) fn has_trailing_comments(&self) -> bool { + self.syntax().has_trailing_comments() + } + + /// Returns true if the member any of it's ancestor parentheses nodes has any leading comments. + pub(crate) fn has_leading_comments(&self) -> SyntaxResult { + let has_operator_comment = match self { + ChainMember::StaticMember(node) => node.operator_token()?.has_leading_comments(), + _ => false, + }; + + Ok(self.syntax().has_leading_comments() || has_operator_comment) + } + /// 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 { match self { 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 dc936581a90..949fc2b1411 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/groups.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/groups.rs @@ -1,7 +1,7 @@ use crate::context::TabWidth; use crate::parentheses::NeedsParentheses; use crate::prelude::*; -use crate::utils::member_chain::chain_member::{ChainEntry, ChainMember}; +use crate::utils::member_chain::chain_member::ChainMember; use rome_formatter::write; use rome_js_syntax::JsCallExpression; use rome_rowan::SyntaxResult; @@ -32,14 +32,14 @@ impl MemberChainGroupsBuilder { } /// starts a new group - pub fn start_group(&mut self, flatten_item: ChainEntry) { - debug_assert!(self.current_group.entries.is_empty()); - self.current_group.entries.push(flatten_item); + pub fn start_group(&mut self, flatten_item: ChainMember) { + debug_assert!(self.current_group.members.is_empty()); + self.current_group.members.push(flatten_item); } /// continues of starts a new group - pub fn start_or_continue_group(&mut self, flatten_item: ChainEntry) { - if self.current_group.entries.is_empty() { + 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); @@ -47,14 +47,14 @@ impl MemberChainGroupsBuilder { } /// adds the passed element to the current group - pub fn continue_group(&mut self, flatten_item: ChainEntry) { - debug_assert!(!self.current_group.entries.is_empty()); - self.current_group.entries.push(flatten_item); + pub fn continue_group(&mut self, flatten_item: ChainMember) { + debug_assert!(!self.current_group.members.is_empty()); + self.current_group.members.push(flatten_item); } /// clears the current group, and adds a new group to the groups pub fn close_group(&mut self) { - if !self.current_group.entries.is_empty() { + 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); @@ -62,7 +62,7 @@ impl MemberChainGroupsBuilder { } pub(super) fn finish(self) -> MemberChainGroups { - debug_assert!(self.current_group.entries().is_empty()); + debug_assert!(self.current_group.members().is_empty()); MemberChainGroups { groups: self.groups, @@ -98,7 +98,7 @@ impl MemberChainGroups { Ok(!self.groups.len() >= 1 && self.should_not_wrap(head_group)? && !self.groups[0] - .entries + .members .first() .map_or(false, |item| item.has_trailing_comments())) } @@ -107,7 +107,7 @@ impl MemberChainGroups { pub fn has_comments(&self) -> SyntaxResult { let mut has_leading_comments = false; - let flat_groups = self.groups.iter().flat_map(|item| item.entries.iter()); + let flat_groups = self.groups.iter().flat_map(|item| item.members.iter()); for item in flat_groups { if item.has_leading_comments()? { has_leading_comments = true; @@ -118,13 +118,13 @@ impl MemberChainGroups { let has_trailing_comments = self .groups .iter() - .flat_map(|item| item.entries.iter()) + .flat_map(|item| item.members.iter()) .any(|item| item.has_trailing_comments()); let cutoff_has_leading_comments = if self.groups.len() >= self.cutoff as usize { let group = self.groups.get(self.cutoff as usize); if let Some(group) = group { - let first_item = group.entries.first(); + let first_item = group.members.first(); if let Some(first_item) = first_item { first_item.has_leading_comments()? } else { @@ -145,9 +145,9 @@ impl MemberChainGroups { pub fn get_call_expressions(&self) -> impl Iterator { self.groups .iter() - .flat_map(|group| group.entries.iter()) + .flat_map(|group| group.members.iter()) .filter_map(|item| { - if let ChainMember::CallExpression(call_expression, ..) = item.member() { + if let ChainMember::CallExpression(call_expression, ..) = item { Some(call_expression) } else { None @@ -163,16 +163,16 @@ impl MemberChainGroups { // SAFETY: guarded by the previous check let group = &self.groups[0]; group - .entries + .members .first() - .map_or(false, |item| item.member().is_computed_expression()) + .map_or(false, |item| item.is_computed_expression()) } else { false }; - if first_group.entries().len() == 1 { + if first_group.members().len() == 1 { // SAFETY: access is guarded by the previous check - let first_node = first_group.entries().first().unwrap().member(); + let first_node = first_group.members().first().unwrap(); return Ok(first_node.is_this_expression() || (first_node.is_identifier_expression() @@ -186,11 +186,9 @@ impl MemberChainGroups { let last_node_is_factory = self .groups .iter() - .flat_map(|group| group.entries.iter()) + .flat_map(|group| group.members.iter()) .last() - .map_or(false, |item| { - item.member().is_factory(false).unwrap_or(false) - }); + .map_or(false, |item| item.is_factory(false).unwrap_or(false)); Ok(last_node_is_factory || has_computed_property) } @@ -233,44 +231,44 @@ impl Format for MemberChainGroups { #[derive(Debug, Clone, Default)] pub(super) struct MemberChainGroup { - entries: Vec, + members: Vec, } impl MemberChainGroup { - pub(super) fn into_entries(self) -> Vec { - self.entries + pub(super) fn into_members(self) -> Vec { + self.members } - fn entries(&self) -> &[ChainEntry] { - &self.entries + fn members(&self) -> &[ChainMember] { + &self.members } - pub(super) fn expand_group(&mut self, group: impl IntoIterator) { - self.entries.extend(group) + pub(super) fn expand_group(&mut self, group: impl IntoIterator) { + self.members.extend(group) } pub(super) fn has_comments(&self) -> bool { - self.entries.iter().any(|item| item.has_trailing_comments()) + self.members.iter().any(|item| item.has_trailing_comments()) } } -impl From> for MemberChainGroup { - fn from(entries: Vec) -> Self { - Self { entries } +impl From> for MemberChainGroup { + fn from(entries: Vec) -> Self { + Self { members: entries } } } impl Format for MemberChainGroup { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let last = self.entries.last(); + let last = self.members.last(); - let needs_parens = last.map_or(false, |last| match last.member() { + let needs_parens = last.map_or(false, |last| match last { ChainMember::StaticMember(member) => member.needs_parentheses(), ChainMember::ComputedMember(member) => member.needs_parentheses(), _ => false, }); - let format_entries = format_with(|f| f.join().entries(self.entries.iter()).finish()); + let format_entries = format_with(|f| f.join().entries(self.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 e6b3dbdd4dc..a0fc01ae863 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/mod.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/mod.rs @@ -106,9 +106,8 @@ mod chain_member; mod groups; mod simple_argument; -use crate::parentheses::NeedsParentheses; use crate::prelude::*; -use crate::utils::member_chain::chain_member::{ChainEntry, ChainMember}; +use crate::utils::member_chain::chain_member::ChainMember; use crate::utils::member_chain::groups::{ MemberChainGroup, MemberChainGroups, MemberChainGroupsBuilder, }; @@ -194,9 +193,10 @@ pub(crate) fn get_member_chain( f: &mut JsFormatter, ) -> SyntaxResult { let mut chain_members = vec![]; - let parent_is_expression_statement = call_expression.resolve_parent().map_or(false, |parent| { - JsExpressionStatement::can_cast(parent.kind()) - }); + let parent_is_expression_statement = + call_expression.syntax().parent().map_or(false, |parent| { + JsExpressionStatement::can_cast(parent.kind()) + }); let root = flatten_member_chain( &mut chain_members, @@ -210,7 +210,7 @@ pub(crate) fn get_member_chain( // will be used later to decide on how to format it let calls_count = chain_members .iter() - .filter(|item| item.member().is_loose_call_expression()) + .filter(|item| item.is_loose_call_expression()) .count(); // as explained before, the first group is particular, so we calculate it @@ -235,7 +235,7 @@ pub(crate) fn get_member_chain( if let Some(group_to_merge) = rest_of_groups.should_merge_with_first_group(&head_group) { let group_to_merge = group_to_merge .into_iter() - .flat_map(|group| group.into_entries()); + .flat_map(|group| group.into_members()); head_group.expand_group(group_to_merge); } @@ -249,7 +249,7 @@ pub(crate) fn get_member_chain( /// 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: &[ChainEntry]) -> usize { +fn compute_first_group_index(flatten_items: &[ChainMember]) -> usize { flatten_items .iter() .enumerate() @@ -257,7 +257,7 @@ fn compute_first_group_index(flatten_items: &[ChainEntry]) -> usize { .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.member() { + 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()()()()` @@ -298,7 +298,7 @@ fn compute_first_group_index(flatten_items: &[ChainEntry]) -> usize { ChainMember::StaticMember(_) => { let next_flatten_item = &flatten_items[index + 1]; matches!( - next_flatten_item.member(), + next_flatten_item, ChainMember::StaticMember(_) | ChainMember::ComputedMember(_) ) } @@ -319,7 +319,7 @@ fn compute_first_group_index(flatten_items: &[ChainEntry]) -> usize { /// computes groups coming after the first group fn compute_groups( - flatten_items: impl Iterator, + flatten_items: impl Iterator, in_expression_statement: bool, f: &JsFormatter, ) -> MemberChainGroups { @@ -327,9 +327,9 @@ fn compute_groups( let mut groups_builder = MemberChainGroupsBuilder::new(in_expression_statement, f.options().tab_width()); for item in flatten_items { - let has_trailing_comments = item.member().syntax().has_trailing_comments(); + let has_trailing_comments = item.syntax().has_trailing_comments(); - match item.member() { + match item { ChainMember::StaticMember(_) => { // if we have seen a JsCallExpression, we want to close the group. // The resultant group will be something like: [ . , then, () ]; @@ -345,7 +345,7 @@ fn compute_groups( } } ChainMember::CallExpression(_) => { - let is_loose_call_expression = item.member().is_loose_call_expression(); + 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; @@ -374,14 +374,14 @@ fn compute_groups( /// 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, + queue: &mut Vec, node: JsAnyExpression, comments: &Comments, -) -> SyntaxResult { +) -> SyntaxResult { use JsAnyExpression::*; if comments.is_suppressed(node.syntax()) { - return Ok(ChainEntry::Member(ChainMember::Node(node.into_syntax()))); + return Ok(ChainMember::Node(node.into_syntax())); } match node { @@ -390,16 +390,14 @@ fn flatten_member_chain( let left = flatten_member_chain(queue, callee, comments)?; queue.push(left); - Ok(ChainEntry::Member(ChainMember::CallExpression( - call_expression, - ))) + Ok(ChainMember::CallExpression(call_expression)) } JsStaticMemberExpression(static_member) => { let object = static_member.object()?; let left = flatten_member_chain(queue, object, comments)?; queue.push(left); - Ok(ChainEntry::Member(ChainMember::StaticMember(static_member))) + Ok(ChainMember::StaticMember(static_member)) } JsComputedMemberExpression(computed_expression) => { @@ -408,21 +406,9 @@ fn flatten_member_chain( let left = flatten_member_chain(queue, object, comments)?; queue.push(left); - Ok(ChainEntry::Member(ChainMember::ComputedMember( - computed_expression, - ))) + Ok(ChainMember::ComputedMember(computed_expression)) } - JsParenthesizedExpression(parenthesized) => { - let inner = flatten_member_chain(queue, parenthesized.expression()?, comments)?; - - Ok(ChainEntry::Parenthesized { - member: inner.into_member(), - top_most_parentheses: parenthesized, - }) - } - expression => Ok(ChainEntry::Member(ChainMember::Node( - expression.into_syntax(), - ))), + expression => Ok(ChainMember::Node(expression.into_syntax())), } } diff --git a/crates/rome_js_formatter/tests/prettier_tests.rs b/crates/rome_js_formatter/tests/prettier_tests.rs index faeb97f6cad..3f1cf1303f4 100644 --- a/crates/rome_js_formatter/tests/prettier_tests.rs +++ b/crates/rome_js_formatter/tests/prettier_tests.rs @@ -15,7 +15,7 @@ use std::{ }; use rome_diagnostics::{file::SimpleFiles, termcolor, Emitter}; -use rome_formatter::IndentStyle; +use rome_formatter::{FormatOptions, IndentStyle}; use rome_js_formatter::context::JsFormatOptions; use rome_js_parser::parse; use rome_js_syntax::SourceType; diff --git a/crates/rome_js_formatter/tests/spec_test.rs b/crates/rome_js_formatter/tests/spec_test.rs index ac6047fe50e..3f119ecbf35 100644 --- a/crates/rome_js_formatter/tests/spec_test.rs +++ b/crates/rome_js_formatter/tests/spec_test.rs @@ -1,4 +1,4 @@ -use rome_formatter::LineWidth; +use rome_formatter::{FormatOptions, LineWidth}; use rome_formatter::{IndentStyle, Printed}; use rome_fs::RomePath; use rome_js_formatter::context::{JsFormatOptions, QuoteProperties, QuoteStyle}; diff --git a/crates/rome_js_formatter/tests/specs/js/module/comments.js.snap b/crates/rome_js_formatter/tests/specs/js/module/comments.js.snap index e86b472ab5d..3a280bed2b0 100644 --- a/crates/rome_js_formatter/tests/specs/js/module/comments.js.snap +++ b/crates/rome_js_formatter/tests/specs/js/module/comments.js.snap @@ -156,8 +156,8 @@ function name( 4 + /* plus trailing */ 3 * 2 /* 2 trailing */; -/* leading of opening */ /* trailing of opening */ 4 + 3 -/* leading of closing */ /* trailing of closing */; +/* leading of opening */ /* trailing of opening */ 4 + + 3 /* leading of closing */ /* trailing of closing */; [3 /* trailing num */ /* trailing sep */]; diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/binary-expr.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/binary-expr.js.snap deleted file mode 100644 index b14dc3f4a22..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/binary-expr.js.snap +++ /dev/null @@ -1,30 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -var a = b || /** @type {string} */ - (c); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1 +1 @@ --var a = b || /** @type {string} */ (c); -+var a = b || /** @type {string} */ c; -``` - -# Output - -```js -var a = b || /** @type {string} */ c; -``` - - - 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 index bfeea10da12..53686ecb0e7 100644 --- 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 @@ -73,79 +73,14 @@ const style2 =/** ```diff --- Prettier +++ Rome -@@ -9,35 +9,35 @@ - }; - - // preserve parens only for type casts --let assignment = /** @type {string} */ (getValue()); --let value = /** @type {string} */ (this.members[0]).functionCall(); -+let assignment = /** @type {string} */ getValue(); -+let value = /** @type {string} */ this.members[0].functionCall(); - --functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); -+functionCall(1 + /** @type {string} */ value, /** @type {!Foo} */ {}); - - function returnValue() { -- return /** @type {!Array.} */ (["hello", "you"]); -+ 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)); -+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))); -+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); -+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), -+ /** @type {{height: number, width: number}} */ arg3, - ); - - const style = /** @type {{ -@@ -47,16 +47,16 @@ - marginLeft: number, - marginRight: number, - marginBottom: number, --}} */ ({ -+}} */ { - width, - height, - ...margins, --}); -+}; - - const style2 = /** +@@ -57,6 +57,6 @@ * @type {{ * width: number, * }} - */ ({ -+*/ { ++*/ ({ width, --}); -+}; + }); ``` # Output @@ -162,35 +97,35 @@ let object = { }; // preserve parens only for type casts -let assignment = /** @type {string} */ getValue(); -let value = /** @type {string} */ this.members[0].functionCall(); +let assignment = /** @type {string} */ (getValue()); +let value = /** @type {string} */ (this.members[0]).functionCall(); -functionCall(1 + /** @type {string} */ value, /** @type {!Foo} */ {}); +functionCall(1 + /** @type {string} */ (value), /** @type {!Foo} */ ({})); function returnValue() { - return /** @type {!Array.} */ ["hello", "you"]; + 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)); +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)); +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); +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, + /** @type {{height: number, width: number}} */ (arg3), ); const style = /** @type {{ @@ -200,19 +135,19 @@ const style = /** @type {{ 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 index d61bb653ac9..9d5e8c2aae1 100644 --- 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 @@ -24,9 +24,8 @@ console.log(a.foo()); ```diff --- Prettier +++ Rome -@@ -1,11 +1,12 @@ +@@ -1,11 +1,11 @@ var a = -+ window /** - * bla bla bla - * @type {string | @@ -41,8 +40,7 @@ console.log(a.foo()); +* bla bla bla + */ //2 -- (window["s"]).toString(); -+ ["s"].toString(); + (window["s"]).toString(); console.log(a.foo()); ``` @@ -50,7 +48,6 @@ console.log(a.foo()); ```js var a = - window /** * bla bla bla * @type {string | @@ -59,7 +56,7 @@ var a = * bla bla bla */ //2 - ["s"].toString(); + (window["s"]).toString(); console.log(a.foo()); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-placement.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-placement.js.snap index d33f9a5c481..053c9cf6526 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-placement.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/comment-placement.js.snap @@ -33,47 +33,37 @@ const foo5 = ```diff --- Prettier +++ Rome -@@ -1,13 +1,15 @@ --const foo1 = /** @type {string} */ (value); -+const foo1 = /** @type {string} */ value; - - const foo2 = - /** @type {string} */ -- (value); -+ value; - - const foo3 = +@@ -8,6 +8,8 @@ /** @type {string} */ -- (value); -+ value; + (value); -const foo4 = /** @type {string} */ (value); +const foo4 = -+ /** @type {string} */ value; ++ /** @type {string} */ (value); -const foo5 = /** @type {string} */ (value); +const foo5 = -+ /** @type {string} */ value; ++ /** @type {string} */ (value); ``` # Output ```js -const foo1 = /** @type {string} */ value; +const foo1 = /** @type {string} */ (value); const foo2 = /** @type {string} */ - value; + (value); const foo3 = /** @type {string} */ - value; + (value); const foo4 = - /** @type {string} */ value; + /** @type {string} */ (value); const foo5 = - /** @type {string} */ value; + /** @type {string} */ (value); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/extra-spaces-and-asterisks.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/extra-spaces-and-asterisks.js.snap deleted file mode 100644 index 3b3d0662a35..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/extra-spaces-and-asterisks.js.snap +++ /dev/null @@ -1,57 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const foo1 = /** @type {!Foo} */(bar); -const foo2 = /** @type {!Foo} **/(bar); -const foo3 = /** @type {!Foo} * */(bar); -const foo4 = /** @type {!Foo} ***/(bar); -const foo5 = /** @type {!Foo} * * */(bar); -const foo6 = /** @type {!Foo} *****/(bar); -const foo7 = /** @type {!Foo} * * * * */(bar); -const foo8 = /** @type {!Foo} ** * * */(bar); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,8 +1,8 @@ --const foo1 = /** @type {!Foo} */ (bar); --const foo2 = /** @type {!Foo} **/ (bar); --const foo3 = /** @type {!Foo} * */ (bar); --const foo4 = /** @type {!Foo} ***/ (bar); --const foo5 = /** @type {!Foo} * * */ (bar); --const foo6 = /** @type {!Foo} *****/ (bar); --const foo7 = /** @type {!Foo} * * * * */ (bar); --const foo8 = /** @type {!Foo} ** * * */ (bar); -+const foo1 = /** @type {!Foo} */ bar; -+const foo2 = /** @type {!Foo} **/ bar; -+const foo3 = /** @type {!Foo} * */ bar; -+const foo4 = /** @type {!Foo} ***/ bar; -+const foo5 = /** @type {!Foo} * * */ bar; -+const foo6 = /** @type {!Foo} *****/ bar; -+const foo7 = /** @type {!Foo} * * * * */ bar; -+const foo8 = /** @type {!Foo} ** * * */ bar; -``` - -# Output - -```js -const foo1 = /** @type {!Foo} */ bar; -const foo2 = /** @type {!Foo} **/ bar; -const foo3 = /** @type {!Foo} * */ bar; -const foo4 = /** @type {!Foo} ***/ bar; -const foo5 = /** @type {!Foo} * * */ bar; -const foo6 = /** @type {!Foo} *****/ bar; -const foo7 = /** @type {!Foo} * * * * */ bar; -const foo8 = /** @type {!Foo} ** * * */ bar; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/iife.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/iife.js.snap deleted file mode 100644 index 728349da835..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/iife.js.snap +++ /dev/null @@ -1,67 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const helpers1 = /** @type {Helpers} */ (( - (helpers = {}) => helpers -)()); - -const helpers2 = /** @type {Helpers} */ (( - function() { return something } -)()); - -// TODO: @param is misplaced https://github.com/prettier/prettier/issues/5850 -const helpers = /** @type {Helpers} */ (( - /** @param {Partial} helpers */ - (helpers = {}) => helpers -)()); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,13 +1,10 @@ --const helpers1 = /** @type {Helpers} */ (((helpers = {}) => helpers)()); -+const helpers1 = /** @type {Helpers} */ ((helpers = {}) => helpers)(); - --const helpers2 = /** @type {Helpers} */ ( -- (function () { -- return something; -- })() --); -+const helpers2 = /** @type {Helpers} */ (function () { -+ return something; -+})(); - - // TODO: @param is misplaced https://github.com/prettier/prettier/issues/5850 --const helpers = /** @type {Helpers} */ ( -+const helpers = /** @type {Helpers} */ - /** @param {Partial} helpers */ -- ((helpers = {}) => helpers)() --); -+ ((helpers = {}) => helpers)(); -``` - -# Output - -```js -const helpers1 = /** @type {Helpers} */ ((helpers = {}) => helpers)(); - -const helpers2 = /** @type {Helpers} */ (function () { - return something; -})(); - -// TODO: @param is misplaced https://github.com/prettier/prettier/issues/5850 -const helpers = /** @type {Helpers} */ - /** @param {Partial} helpers */ - ((helpers = {}) => helpers)(); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-4124.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-4124.js.snap index c561aea9791..96b30f08c0c 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-4124.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-4124.js.snap @@ -29,67 +29,42 @@ const test = /** @type (function (*): ?|undefined) */ (foo); ```diff --- Prettier +++ Rome -@@ -1,18 +1,22 @@ --/** @type {Object} */ (myObject.property).someProp = true; --/** @type {Object} */ (myObject.property).someProp = true; -+/** @type {Object} */ myObject.property.someProp = true; -+/** @type {Object} */ myObject.property.someProp = true; +@@ -3,9 +3,9 @@ --const prop = /** @type {Object} */ (myObject.property).someProp; -+const prop = /** @type {Object} */ myObject.property.someProp; + const prop = /** @type {Object} */ (myObject.property).someProp; -const test = - /** @type (function (*): ?|undefined) */ - (goog.partial(NewThing.onTemplateChange, rationaleField, typeField)); -+const test = /** @type (function (*): ?|undefined) */ goog.partial( -+ NewThing.onTemplateChange, -+ rationaleField, -+ typeField, ++const test = /** @type (function (*): ?|undefined) */ ( ++ goog.partial(NewThing.onTemplateChange, rationaleField, typeField) +); --const test = /** @type (function (*): ?|undefined) */ ( -- goog.partial(NewThing.onTemplateChange, rationaleField, typeField) -+const test = /** @type (function (*): ?|undefined) */ goog.partial( -+ NewThing.onTemplateChange, -+ rationaleField, -+ typeField, - ); - --const model = /** @type {?{getIndex: Function}} */ (model); -+const model = /** @type {?{getIndex: Function}} */ model; - --const foo = /** @type {string} */ (bar); -+const foo = /** @type {string} */ bar; - --const test = /** @type (function (*): ?|undefined) */ (foo); -+const test = /** @type (function (*): ?|undefined) */ foo; + const test = /** @type (function (*): ?|undefined) */ ( + goog.partial(NewThing.onTemplateChange, rationaleField, typeField) ``` # Output ```js -/** @type {Object} */ myObject.property.someProp = true; -/** @type {Object} */ myObject.property.someProp = true; +/** @type {Object} */ (myObject.property).someProp = true; +/** @type {Object} */ (myObject.property).someProp = true; -const prop = /** @type {Object} */ myObject.property.someProp; +const prop = /** @type {Object} */ (myObject.property).someProp; -const test = /** @type (function (*): ?|undefined) */ goog.partial( - NewThing.onTemplateChange, - rationaleField, - typeField, +const test = /** @type (function (*): ?|undefined) */ ( + goog.partial(NewThing.onTemplateChange, rationaleField, typeField) ); -const test = /** @type (function (*): ?|undefined) */ goog.partial( - NewThing.onTemplateChange, - rationaleField, - typeField, +const test = /** @type (function (*): ?|undefined) */ ( + goog.partial(NewThing.onTemplateChange, rationaleField, typeField) ); -const model = /** @type {?{getIndex: Function}} */ model; +const model = /** @type {?{getIndex: Function}} */ (model); -const foo = /** @type {string} */ bar; +const foo = /** @type {string} */ (bar); -const test = /** @type (function (*): ?|undefined) */ foo; +const test = /** @type (function (*): ?|undefined) */ (foo); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-8045.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-8045.js.snap index 3b9bf50f337..94bf9a77f64 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-8045.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-8045.js.snap @@ -34,45 +34,42 @@ function jsdocCastInReturn() { ```diff --- Prettier +++ Rome -@@ -1,30 +1,20 @@ +@@ -1,5 +1,5 @@ -const myLongVariableName = - /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ (fooBarBaz); +const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ -+ fooBarBaz; ++ (fooBarBaz); function jsdocCastInReturn() { -- return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ ( -- fooBarBaz -- ); -+ return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; + return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ ( +@@ -7,24 +7,20 @@ + ); } -const myLongVariableName = - /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ -- (fooBarBaz); +const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ -+ fooBarBaz; + (fooBarBaz); function jsdocCastInReturn() { -- return ( + return ( - /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - (fooBarBaz) -- ); -+ return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; ++ /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ (fooBarBaz) + ); } -const myLongVariableName = - /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ -- (fooBarBaz); +const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ -+ fooBarBaz; + (fooBarBaz); function jsdocCastInReturn() { -- return ( + return ( - /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - (fooBarBaz) -- ); -+ return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; ++ /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ (fooBarBaz) + ); } ``` @@ -80,24 +77,30 @@ function jsdocCastInReturn() { ```js const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - fooBarBaz; + (fooBarBaz); function jsdocCastInReturn() { - return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; + return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ ( + fooBarBaz + ); } const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - fooBarBaz; + (fooBarBaz); function jsdocCastInReturn() { - return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; + return ( + /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ (fooBarBaz) + ); } const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - fooBarBaz; + (fooBarBaz); function jsdocCastInReturn() { - return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; + return ( + /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ (fooBarBaz) + ); } ``` @@ -105,10 +108,7 @@ function jsdocCastInReturn() { # Lines exceeding max width of 80 characters ``` 1: const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - 5: return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; - 8: const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - 12: return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; - 15: const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ - 19: return /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ fooBarBaz; + 10: const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ + 19: const myLongVariableName = /** @type {ThisIsAVeryLongTypeThatShouldTriggerLineWrapping} */ ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-9358.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-9358.js.snap deleted file mode 100644 index 36b5e1881cd..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/issue-9358.js.snap +++ /dev/null @@ -1,51 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const fooooba1 = /** @type {Array.} */ (fooobaarbazzItems || foo); -const fooooba2 = /** @type {Array.} */ (fooobaarbazzItems + foo); -const fooooba3 = /** @type {Array.} */ (fooobaarbazzItems || foo) ? foo : bar; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,11 +1,6 @@ --const fooooba1 = /** @type {Array.} */ ( -- fooobaarbazzItems || foo --); --const fooooba2 = /** @type {Array.} */ ( -- fooobaarbazzItems + foo --); --const fooooba3 = /** @type {Array.} */ ( -- fooobaarbazzItems || foo --) -- ? foo -- : bar; -+const fooooba1 = /** @type {Array.} */ -+ fooobaarbazzItems || foo; -+const fooooba2 = /** @type {Array.} */ -+ fooobaarbazzItems + foo; -+const fooooba3 = /** @type {Array.} */ -+ fooobaarbazzItems || foo ? foo : bar; -``` - -# Output - -```js -const fooooba1 = /** @type {Array.} */ - fooobaarbazzItems || foo; -const fooooba2 = /** @type {Array.} */ - fooobaarbazzItems + foo; -const fooooba3 = /** @type {Array.} */ - fooobaarbazzItems || foo ? foo : bar; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/member.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/member.js.snap deleted file mode 100644 index 685fe607292..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/member.js.snap +++ /dev/null @@ -1,29 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -foo = (/** @type {!Baz} */ (baz).bar); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1 +1 @@ --foo = /** @type {!Baz} */ (baz).bar; -+foo = /** @type {!Baz} */ baz.bar; -``` - -# Output - -```js -foo = /** @type {!Baz} */ baz.bar; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/nested.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/nested.js.snap deleted file mode 100644 index 4ec2cee6b97..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/nested.js.snap +++ /dev/null @@ -1,55 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -foo = /** @type {!Foo} */ (/** @type {!Baz} */ (baz).bar ); - -const BarImpl = /** @type {BarConstructor} */ ( - /** @type {unknown} */ - (function Bar() { - throw new Error("Internal error: Illegal constructor"); - }) -); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,10 +1,7 @@ --foo = /** @type {!Foo} */ (/** @type {!Baz} */ (baz).bar); -+foo = /** @type {!Foo} */ /** @type {!Baz} */ baz.bar; - --const BarImpl = /** @type {BarConstructor} */ ( -+const BarImpl = /** @type {BarConstructor} */ - /** @type {unknown} */ -- ( -- function Bar() { -- throw new Error("Internal error: Illegal constructor"); -- } -- ) --); -+ function Bar() { -+ throw new Error("Internal error: Illegal constructor"); -+ }; -``` - -# Output - -```js -foo = /** @type {!Foo} */ /** @type {!Baz} */ baz.bar; - -const BarImpl = /** @type {BarConstructor} */ - /** @type {unknown} */ - function Bar() { - throw new Error("Internal error: Illegal constructor"); - }; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/object-with-comment.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/object-with-comment.js.snap index 15bafcfd80f..40b34891364 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/object-with-comment.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/object-with-comment.js.snap @@ -24,38 +24,34 @@ const objectWithComment2 = /** @type MyType */ ( /* comment */ { ```diff --- Prettier +++ Rome -@@ -1,12 +1,9 @@ --const objectWithComment = /** @type MyType */ ( -+const objectWithComment = /** @type MyType */ - /* comment */ - { - foo: bar, -- } --); -+ }; +@@ -5,8 +5,8 @@ + } + ); -const objectWithComment2 = /** @type MyType */ ( - /* comment */ { -- foo: bar, -- } --); -+const objectWithComment2 = /** @type MyType */ /* comment */ { -+ foo: bar, -+}; ++const objectWithComment2 = /** @type MyType */ (/* comment */ ++ { + foo: bar, + } + ); ``` # Output ```js -const objectWithComment = /** @type MyType */ +const objectWithComment = /** @type MyType */ ( /* comment */ { foo: bar, - }; + } +); -const objectWithComment2 = /** @type MyType */ /* comment */ { - foo: bar, -}; +const objectWithComment2 = /** @type MyType */ (/* comment */ + { + foo: bar, + } +); ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/styled-components.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/styled-components.js.snap index 93a6b1a6a44..95bab75730e 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/styled-components.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/styled-components.js.snap @@ -26,9 +26,8 @@ top: ${p => p.overlap === 'next' && 0}; @@ -1,10 +1,10 @@ const OverlapWrapper = /** @type {import('styled-components').ThemedStyledFunction<'div',null,{overlap: boolean}>} */ -- (styled.div)` + (styled.div)` - position: relative; -+ styled.div` +position:relative; > { - position: absolute; @@ -48,7 +47,7 @@ top: ${p => p.overlap === 'next' && 0}; ```js const OverlapWrapper = /** @type {import('styled-components').ThemedStyledFunction<'div',null,{overlap: boolean}>} */ - styled.div` + (styled.div)` position:relative; > { position: absolute; diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/superclass.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/superclass.js.snap deleted file mode 100644 index db1c0a6b5a5..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/superclass.js.snap +++ /dev/null @@ -1,29 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -class Foo extends /** @type {string} */ (Bar) {} -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1 +1 @@ --class Foo extends /** @type {string} */ (Bar) {} -+class Foo extends /** @type {string} */ Bar {} -``` - -# Output - -```js -class Foo extends /** @type {string} */ Bar {} -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/ways-to-specify-type.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/ways-to-specify-type.js.snap deleted file mode 100644 index a348cb594f0..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments-closure-typecast/ways-to-specify-type.js.snap +++ /dev/null @@ -1,75 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const curlyBraces = /** @type {string} */ (foo); -const curlyBraces2 = /**@type {string} */ (foo); -const noWhitespace = /** @type{string} */ (foo); -const noWhitespace2 = /**@type{string} */ (foo); -const noBraces = /** @type string */ (foo); -const parens = /** @type (string | number) */ (foo); - -// Prettier just searches for "@type" and doesn't check the syntax of types. -const v1 = /** @type {} */ (value); -const v2 = /** @type {}} */ (value); -const v3 = /** @type } */ (value); -const v4 = /** @type { */ (value); -const v5 = /** @type {{} */ (value); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,13 +1,13 @@ --const curlyBraces = /** @type {string} */ (foo); --const curlyBraces2 = /**@type {string} */ (foo); --const noWhitespace = /** @type{string} */ (foo); --const noWhitespace2 = /**@type{string} */ (foo); --const noBraces = /** @type string */ (foo); --const parens = /** @type (string | number) */ (foo); -+const curlyBraces = /** @type {string} */ foo; -+const curlyBraces2 = /**@type {string} */ foo; -+const noWhitespace = /** @type{string} */ foo; -+const noWhitespace2 = /**@type{string} */ foo; -+const noBraces = /** @type string */ foo; -+const parens = /** @type (string | number) */ foo; - - // Prettier just searches for "@type" and doesn't check the syntax of types. --const v1 = /** @type {} */ (value); --const v2 = /** @type {}} */ (value); --const v3 = /** @type } */ (value); --const v4 = /** @type { */ (value); --const v5 = /** @type {{} */ (value); -+const v1 = /** @type {} */ value; -+const v2 = /** @type {}} */ value; -+const v3 = /** @type } */ value; -+const v4 = /** @type { */ value; -+const v5 = /** @type {{} */ value; -``` - -# Output - -```js -const curlyBraces = /** @type {string} */ foo; -const curlyBraces2 = /**@type {string} */ foo; -const noWhitespace = /** @type{string} */ foo; -const noWhitespace2 = /**@type{string} */ foo; -const noBraces = /** @type string */ foo; -const parens = /** @type (string | number) */ foo; - -// Prettier just searches for "@type" and doesn't check the syntax of types. -const v1 = /** @type {} */ value; -const v2 = /** @type {}} */ value; -const v3 = /** @type } */ value; -const v4 = /** @type { */ value; -const v5 = /** @type {{} */ value; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments/function-declaration.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments/function-declaration.js.snap index 4cc3745827c..6e2a946131b 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments/function-declaration.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments/function-declaration.js.snap @@ -74,14 +74,17 @@ function foo4() { ```diff --- Prettier +++ Rome -@@ -3,34 +3,32 @@ +@@ -3,34 +3,35 @@ function c(/* comment */ argA, argB, argC) {} // comment call((/*object*/ row) => {}); KEYPAD_NUMBERS.map( - ( - num, // Buttons 0-9 - ) =>
, -+ (num) =>
, // Buttons 0-9 ++ (num) => ( ++ // Buttons 0-9 ++
++ ), ); -function f1 /* f */() {} @@ -120,7 +123,7 @@ function foo4() { } class C2 { f(/* args */) {} -@@ -39,11 +37,12 @@ +@@ -39,11 +40,12 @@ f() /* returns */ {} } class C4 { @@ -146,7 +149,10 @@ function b() {} // comment function c(/* comment */ argA, argB, argC) {} // comment call((/*object*/ row) => {}); KEYPAD_NUMBERS.map( - (num) =>
, // Buttons 0-9 + (num) => ( + // Buttons 0-9 +
+ ), ); function f1 /* f */ () {} diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/comments/return-statement.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/comments/return-statement.js.snap index 107525ce77e..2c9d24ea4c5 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/comments/return-statement.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/comments/return-statement.js.snap @@ -151,17 +151,6 @@ function inlineComment() { } function excessiveEverything() { -@@ -103,8 +105,8 @@ - - function sequenceExpressionInside() { - return ( -- // Reason for a -- a, b -+ a, // Reason for a -+ b - ); - } - @@ -116,5 +118,7 @@ } @@ -283,8 +272,8 @@ function excessiveEverything() { function sequenceExpressionInside() { return ( - a, // Reason for a - b + // Reason for a + a, b ); } diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/issue-7024.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/issue-7024.js.snap deleted file mode 100644 index 7f5a56432c2..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/issue-7024.js.snap +++ /dev/null @@ -1,39 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const radioSelectedAttr = - (isAnyValueSelected && - node.getAttribute(radioAttr.toLowerCase()) === radioValue) || - ((!isAnyValueSelected && values[a].default === true) || a === 0); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,5 +1,4 @@ - const radioSelectedAttr = - (isAnyValueSelected && - node.getAttribute(radioAttr.toLowerCase()) === radioValue) || -- (!isAnyValueSelected && values[a].default === true) || -- a === 0; -+ ((!isAnyValueSelected && values[a].default === true) || a === 0); -``` - -# Output - -```js -const radioSelectedAttr = - (isAnyValueSelected && - node.getAttribute(radioAttr.toLowerCase()) === radioValue) || - ((!isAnyValueSelected && values[a].default === true) || a === 0); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/logical_expression_operators.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/logical_expression_operators.js.snap deleted file mode 100644 index 617b0c8a80d..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/logical_expressions/logical_expression_operators.js.snap +++ /dev/null @@ -1,128 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -// Same operators do not require parens -(foo && bar) && baz; -foo && (bar && baz); -foo && ((bar && baz) && qux); -foo && (bar && (baz && qux)); -foo && (bar && ((baz && qux) && xyz)); -foo && (bar && (baz && (qux && xyz))); - -(foo || bar) || baz; -foo || (bar || baz); -foo || ((bar || baz) || qux); -foo || (bar || (baz || qux)); -foo || (bar || ((baz || qux) || xyz)); -foo || (bar || (baz || (qux || xyz))); - -(foo ?? bar) ?? baz; -foo ?? (bar ?? baz); -foo ?? ((bar ?? baz) ?? qux); -foo ?? (bar ?? (baz ?? qux)); -foo ?? (bar ?? ((baz ?? qux) ?? xyz)); -foo ?? (bar ?? (baz ?? (qux ?? xyz))); - -// Explicitly parenthesized && and || requires parens -(foo && bar) || baz; -(foo || bar) && baz; - -foo && (bar || baz); -foo || (bar && baz); - -// Implicitly parenthesized && and || requires parens -foo && bar || baz; -foo || bar && baz; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,24 +1,24 @@ - // Same operators do not require parens - foo && bar && baz; --foo && bar && baz; --foo && bar && baz && qux; --foo && bar && baz && qux; --foo && bar && baz && qux && xyz; --foo && bar && baz && qux && xyz; -+foo && (bar && baz); -+foo && (bar && baz && qux); -+foo && (bar && (baz && qux)); -+foo && (bar && (baz && qux && xyz)); -+foo && (bar && (baz && (qux && xyz))); - - foo || bar || baz; --foo || bar || baz; --foo || bar || baz || qux; --foo || bar || baz || qux; --foo || bar || baz || qux || xyz; --foo || bar || baz || qux || xyz; -+foo || (bar || baz); -+foo || (bar || baz || qux); -+foo || (bar || (baz || qux)); -+foo || (bar || (baz || qux || xyz)); -+foo || (bar || (baz || (qux || xyz))); - --foo ?? bar ?? baz; - foo ?? bar ?? baz; --foo ?? bar ?? baz ?? qux; --foo ?? bar ?? baz ?? qux; --foo ?? bar ?? baz ?? qux ?? xyz; --foo ?? bar ?? baz ?? qux ?? xyz; -+foo ?? (bar ?? baz); -+foo ?? (bar ?? baz ?? qux); -+foo ?? (bar ?? (baz ?? qux)); -+foo ?? (bar ?? (baz ?? qux ?? xyz)); -+foo ?? (bar ?? (baz ?? (qux ?? xyz))); - - // Explicitly parenthesized && and || requires parens - (foo && bar) || baz; -``` - -# Output - -```js -// Same operators do not require parens -foo && bar && baz; -foo && (bar && baz); -foo && (bar && baz && qux); -foo && (bar && (baz && qux)); -foo && (bar && (baz && qux && xyz)); -foo && (bar && (baz && (qux && xyz))); - -foo || bar || baz; -foo || (bar || baz); -foo || (bar || baz || qux); -foo || (bar || (baz || qux)); -foo || (bar || (baz || qux || xyz)); -foo || (bar || (baz || (qux || xyz))); - -foo ?? bar ?? baz; -foo ?? (bar ?? baz); -foo ?? (bar ?? baz ?? qux); -foo ?? (bar ?? (baz ?? qux)); -foo ?? (bar ?? (baz ?? qux ?? xyz)); -foo ?? (bar ?? (baz ?? (qux ?? xyz))); - -// Explicitly parenthesized && and || requires parens -(foo && bar) || baz; -(foo || bar) && baz; - -foo && (bar || baz); -foo || (bar && baz); - -// Implicitly parenthesized && and || requires parens -(foo && bar) || baz; -foo || (bar && baz); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/nullish-coalescing/nullish_coalesing_operator.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/nullish-coalescing/nullish_coalesing_operator.js.snap deleted file mode 100644 index e199ad174ff..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/js/nullish-coalescing/nullish_coalesing_operator.js.snap +++ /dev/null @@ -1,77 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -obj.foo ?? "default"; - -const x = (foo, bar = foo ?? bar) => {}; - -foo ? bar ?? foo : baz; - -foo ?? (bar ?? baz); -(foo ?? bar) ?? baz; - -// Mixing ?? and (&& or ||) requires parens -// It's a syntax error without it. -(foo ?? baz) || baz; -foo ?? (baz || baz); - -(foo ?? baz) && baz; -foo ?? (baz && baz); - -(foo || baz) ?? baz; -foo || (baz ?? baz); - -(foo && baz) ?? baz; -foo && (baz ?? baz); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -4,7 +4,7 @@ - - foo ? bar ?? foo : baz; - --foo ?? bar ?? baz; -+foo ?? (bar ?? baz); - foo ?? bar ?? baz; - - // Mixing ?? and (&& or ||) requires parens -``` - -# Output - -```js -obj.foo ?? "default"; - -const x = (foo, bar = foo ?? bar) => {}; - -foo ? bar ?? foo : baz; - -foo ?? (bar ?? baz); -foo ?? bar ?? baz; - -// Mixing ?? and (&& or ||) requires parens -// It's a syntax error without it. -(foo ?? baz) || baz; -foo ?? (baz || baz); - -(foo ?? baz) && baz; -foo ?? (baz && baz); - -(foo || baz) ?? baz; -foo || (baz ?? baz); - -(foo && baz) ?? baz; -foo && (baz ?? baz); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/js/unary-expression/comments.js.snap b/crates/rome_js_formatter/tests/specs/prettier/js/unary-expression/comments.js.snap index 81531f7dacb..5c661aa0bee 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/js/unary-expression/comments.js.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/js/unary-expression/comments.js.snap @@ -298,14 +298,15 @@ async function bar2() { ```diff --- Prettier +++ Rome -@@ -1,283 +1,210 @@ +@@ -1,86 +1,59 @@ !x; -!(x /* foo */); --!(/* foo */ x); --!( -- /* foo */ -- x --); ++!x /* foo */; + !(/* foo */ x); + !( + /* foo */ + x + ); -!( - x - /* foo */ @@ -314,22 +315,16 @@ async function bar2() { - x // foo -); +!x /* foo */; -+! /* foo */ x; -+! -+/* foo */ -+x; -+!x -+/* foo */ -+; +!x; // foo !(x + y); -!((x + y) /* foo */); --!(/* foo */ (x + y)); --!( -- /* foo */ -- (x + y) --); ++!(x + y) /* foo */; + !(/* foo */ (x + y)); + !( + /* foo */ + (x + y) + ); -!( - (x + y) - /* foo */ @@ -338,22 +333,16 @@ async function bar2() { - (x + y) // foo -); +!(x + y) /* foo */; -+! /* foo */ (x + y); -+! -+/* foo */ -+(x + y); -+!(x + y) -+/* foo */ -+; +!(x + y); // foo !(x || y); --!(/* foo */ (x || y)); + !(/* foo */ (x || y)); -!((x || y) /* foo */); --!( -- /* foo */ -- (x || y) --); ++!(x || y) /* foo */; + !( + /* foo */ + (x || y) + ); -!( - (x || y) - /* foo */ @@ -361,23 +350,17 @@ async function bar2() { -!( - (x || y) // foo -); -+! /* foo */ (x || y); +!(x || y) /* foo */; -+! -+/* foo */ -+(x || y); -+!(x || y) -+/* foo */ -+; +!(x || y); // foo ![1, 2, 3]; -!([1, 2, 3] /* foo */); --!(/* foo */ [1, 2, 3]); --!( -- /* foo */ -- [1, 2, 3] --); ++![1, 2, 3] /* foo */; + !(/* foo */ [1, 2, 3]); + !( + /* foo */ + [1, 2, 3] + ); -!( - [1, 2, 3] - /* foo */ @@ -386,22 +369,16 @@ async function bar2() { - [1, 2, 3] // foo -); +![1, 2, 3] /* foo */; -+! /* foo */ [1, 2, 3]; -+! -+/* foo */ -+[1, 2, 3]; -+![1, 2, 3] -+/* foo */ -+; +![1, 2, 3]; // foo !{ a: 1, b: 2 }; -!({ a: 1, b: 2 } /* foo */); --!(/* foo */ { a: 1, b: 2 }); --!( -- /* foo */ -- { a: 1, b: 2 } --); ++!{ a: 1, b: 2 } /* foo */; + !(/* foo */ { a: 1, b: 2 }); + !( + /* foo */ + { a: 1, b: 2 } + ); -!( - { a: 1, b: 2 } - /* foo */ @@ -410,13 +387,6 @@ async function bar2() { - { a: 1, b: 2 } // foo -); +!{ a: 1, b: 2 } /* foo */; -+! /* foo */ { a: 1, b: 2 }; -+! -+/* foo */ -+{ a: 1, b: 2 }; -+!{ a: 1, b: 2 } -+/* foo */ -+; +!{ a: 1, b: 2 }; // foo !function () { @@ -427,17 +397,16 @@ async function bar2() { - return x; - } /* foo */ -); --!( -- /* foo */ function () { -- return x; -- } --); --!( -- /* foo */ -- function () { -- return x; -- } --); ++!function () { ++ return x; ++} /* foo */; + !( + /* foo */ function () { + return x; +@@ -92,39 +65,29 @@ + return x; + } + ); -!( - function () { - return x; @@ -452,31 +421,18 @@ async function bar2() { +!function () { + return x; +} /* foo */; -+! -+/* foo */ function () { -+ return x; -+}; -+! -+/* foo */ -+function () { -+ return x; -+}; -+!function () { -+ return x; -+} -+/* foo */ -+; +!function () { + return x; +}; // foo !+3; -!(+3 /* foo */); --!(/* foo */ +3); --!( -- /* foo */ -- +3 --); ++!+3 /* foo */; + !(/* foo */ +3); + !( + /* foo */ + +3 + ); -!( - +3 - /* foo */ @@ -485,28 +441,23 @@ async function bar2() { - +3 // foo -); +!+3 /* foo */; -+! /* foo */ +3; -+! -+/* foo */ -++3; -+!+3 -+/* foo */ -+; +!+3; // foo --!+( -- /* foo */ -- 3 --); + !+( + /* foo */ + 3 + ); -!(/* foo */ +(3 /* foo */)); -!(+(3 /* foo */) /* foo */); --!( -- /* foo */ -- +( -- /* foo */ -- 3 -- ) --); ++!(/* foo */ +3 /* foo */); ++!+3 /* foo */ /* foo */; + !( + /* foo */ + +( +@@ -132,152 +95,99 @@ + 3 + ) + ); -!( - +( - 3 @@ -517,31 +468,17 @@ async function bar2() { -!( - +(3 /* foo */) // foo -); -+!+ -+/* foo */ -+3; -+! /* foo */ +3 /* foo */; +!+3 /* foo */ /* foo */; -+! -+/* foo */ -++ -+/* foo */ -+3; -+!+3 -+/* foo */ -+/* foo */ -+; -+!+3 /* foo */ -+// foo -+; ++!+3 /* foo */; // foo !(x = y); -!((x = y) /* foo */); --!(/* foo */ (x = y)); --!( -- /* foo */ -- (x = y) --); ++!(x = y) /* foo */; + !(/* foo */ (x = y)); + !( + /* foo */ + (x = y) + ); -!( - (x = y) - /* foo */ @@ -550,22 +487,16 @@ async function bar2() { - (x = y) // foo -); +!(x = y) /* foo */; -+! /* foo */ (x = y); -+! -+/* foo */ -+(x = y); -+!(x = y) -+/* foo */ -+; +!(x = y); // foo !x.y; -!(x.y /* foo */); --!(/* foo */ x.y); --!( -- /* foo */ -- x.y --); ++!x.y /* foo */; + !(/* foo */ x.y); + !( + /* foo */ + x.y + ); -!( - x.y - /* foo */ @@ -574,22 +505,16 @@ async function bar2() { - x.y // foo -); +!x.y /* foo */; -+! /* foo */ x.y; -+! -+/* foo */ -+x.y; -+!x.y -+/* foo */ -+; +!x.y; // foo !(x ? y : z); -!((x ? y : z) /* foo */); --!(/* foo */ (x ? y : z)); --!( -- /* foo */ -- (x ? y : z) --); ++!(x ? y : z) /* foo */; + !(/* foo */ (x ? y : z)); + !( + /* foo */ + (x ? y : z) + ); -!( - (x ? y : z) - /* foo */ @@ -598,22 +523,16 @@ async function bar2() { - (x ? y : z) // foo -); +!(x ? y : z) /* foo */; -+! /* foo */ (x ? y : z); -+! -+/* foo */ -+(x ? y : z); -+!(x ? y : z) -+/* foo */ -+; +!(x ? y : z); // foo !x(); -!(x() /* foo */); --!(/* foo */ x()); --!( -- /* foo */ -- x() --); ++!x() /* foo */; + !(/* foo */ x()); + !( + /* foo */ + x() + ); -!( - x() - /* foo */ @@ -622,22 +541,16 @@ async function bar2() { - x() // foo -); +!x() /* foo */; -+! /* foo */ x(); -+! -+/* foo */ -+x(); -+!x() -+/* foo */ -+; +!x(); // foo !new x(); -!(new x() /* foo */); --!(/* foo */ new x()); --!( -- /* foo */ -- new x() --); ++!new x() /* foo */; + !(/* foo */ new x()); + !( + /* foo */ + new x() + ); -!( - new x() - /* foo */ @@ -646,22 +559,16 @@ async function bar2() { - new x() // foo -); +!new x() /* foo */; -+! /* foo */ new x(); -+! -+/* foo */ -+new x(); -+!new x() -+/* foo */ -+; +!new x(); // foo !(x, y); -!((x, y) /* foo */); --!(/* foo */ (x, y)); --!( -- /* foo */ -- (x, y) --); ++!(x, y) /* foo */; + !(/* foo */ (x, y)); + !( + /* foo */ + (x, y) + ); -!( - (x, y) - /* foo */ @@ -670,22 +577,16 @@ async function bar2() { - x.y // foo -); +!(x, y) /* foo */; -+! /* foo */ (x, y); -+! -+/* foo */ -+(x, y); -+!(x, y) -+/* foo */ -+; +!x.y; // foo !(() => 3); -!((() => 3) /* foo */); --!(/* foo */ (() => 3)); --!( -- /* foo */ -- (() => 3) --); ++!(() => 3) /* foo */; + !(/* foo */ (() => 3)); + !( + /* foo */ + (() => 3) + ); -!( - (() => 3) - /* foo */ @@ -694,23 +595,17 @@ async function bar2() { - (() => 3) // foo -); +!(() => 3) /* foo */; -+! /* foo */ (() => 3); -+! -+/* foo */ -+(() => 3); -+!(() => 3) -+/* foo */ -+; +!(() => 3); // foo function* bar() { !(yield x); - !((yield x) /* foo */); -- !(/* foo */ (yield x)); -- !( -- /* foo */ -- (yield x) -- ); ++ !(yield x) /* foo */; + !(/* foo */ (yield x)); + !( + /* foo */ + (yield x) + ); - !( - (yield x) - /* foo */ @@ -719,24 +614,18 @@ async function bar2() { - (yield x) // foo - ); + !(yield x) /* foo */; -+ ! /* foo */ (yield x); -+ ! -+ /* foo */ -+ (yield x); -+ !(yield x) -+ /* foo */ -+ ; + !(yield x); // foo } async function bar2() { !(await x); - !((await x) /* foo */); -- !(/* foo */ (await x)); -- !( -- /* foo */ -- (await x) -- ); ++ !(await x) /* foo */; + !(/* foo */ (await x)); + !( + /* foo */ + (await x) + ); - !( - (await x) - /* foo */ @@ -745,13 +634,6 @@ async function bar2() { - (await x) // foo - ); + !(await x) /* foo */; -+ ! /* foo */ (await x); -+ ! -+ /* foo */ -+ (await x); -+ !(await x) -+ /* foo */ -+ ; + !(await x); // foo } ``` @@ -761,57 +643,52 @@ async function bar2() { ```js !x; !x /* foo */; -! /* foo */ x; -! -/* foo */ -x; -!x -/* foo */ -; +!(/* foo */ x); +!( + /* foo */ + x +); +!x /* foo */; !x; // foo !(x + y); !(x + y) /* foo */; -! /* foo */ (x + y); -! -/* foo */ -(x + y); -!(x + y) -/* foo */ -; +!(/* foo */ (x + y)); +!( + /* foo */ + (x + y) +); +!(x + y) /* foo */; !(x + y); // foo !(x || y); -! /* foo */ (x || y); +!(/* foo */ (x || y)); +!(x || y) /* foo */; +!( + /* foo */ + (x || y) +); !(x || y) /* foo */; -! -/* foo */ -(x || y); -!(x || y) -/* foo */ -; !(x || y); // foo ![1, 2, 3]; ![1, 2, 3] /* foo */; -! /* foo */ [1, 2, 3]; -! -/* foo */ -[1, 2, 3]; -![1, 2, 3] -/* foo */ -; +!(/* foo */ [1, 2, 3]); +!( + /* foo */ + [1, 2, 3] +); +![1, 2, 3] /* foo */; ![1, 2, 3]; // foo !{ a: 1, b: 2 }; !{ a: 1, b: 2 } /* foo */; -! /* foo */ { a: 1, b: 2 }; -! -/* foo */ -{ a: 1, b: 2 }; -!{ a: 1, b: 2 } -/* foo */ -; +!(/* foo */ { a: 1, b: 2 }); +!( + /* foo */ + { a: 1, b: 2 } +); +!{ a: 1, b: 2 } /* foo */; !{ a: 1, b: 2 }; // foo !function () { @@ -820,153 +697,141 @@ x; !function () { return x; } /* foo */; -! -/* foo */ function () { - return x; -}; -! -/* foo */ -function () { - return x; -}; +!( + /* foo */ function () { + return x; + } +); +!( + /* foo */ + function () { + return x; + } +); !function () { return x; -} -/* foo */ -; +} /* foo */; !function () { return x; }; // foo !+3; !+3 /* foo */; -! /* foo */ +3; -! -/* foo */ -+3; -!+3 -/* foo */ -; +!(/* foo */ +3); +!( + /* foo */ + +3 +); +!+3 /* foo */; !+3; // foo -!+ -/* foo */ -3; -! /* foo */ +3 /* foo */; +!+( + /* foo */ + 3 +); +!(/* foo */ +3 /* foo */); !+3 /* foo */ /* foo */; -! -/* foo */ -+ -/* foo */ -3; -!+3 -/* foo */ -/* foo */ -; -!+3 /* foo */ -// foo -; +!( + /* foo */ + +( + /* foo */ + 3 + ) +); +!+3 /* foo */ /* foo */; +!+3 /* foo */; // foo !(x = y); !(x = y) /* foo */; -! /* foo */ (x = y); -! -/* foo */ -(x = y); -!(x = y) -/* foo */ -; +!(/* foo */ (x = y)); +!( + /* foo */ + (x = y) +); +!(x = y) /* foo */; !(x = y); // foo !x.y; !x.y /* foo */; -! /* foo */ x.y; -! -/* foo */ -x.y; -!x.y -/* foo */ -; +!(/* foo */ x.y); +!( + /* foo */ + x.y +); +!x.y /* foo */; !x.y; // foo !(x ? y : z); !(x ? y : z) /* foo */; -! /* foo */ (x ? y : z); -! -/* foo */ -(x ? y : z); -!(x ? y : z) -/* foo */ -; +!(/* foo */ (x ? y : z)); +!( + /* foo */ + (x ? y : z) +); +!(x ? y : z) /* foo */; !(x ? y : z); // foo !x(); !x() /* foo */; -! /* foo */ x(); -! -/* foo */ -x(); -!x() -/* foo */ -; +!(/* foo */ x()); +!( + /* foo */ + x() +); +!x() /* foo */; !x(); // foo !new x(); !new x() /* foo */; -! /* foo */ new x(); -! -/* foo */ -new x(); -!new x() -/* foo */ -; +!(/* foo */ new x()); +!( + /* foo */ + new x() +); +!new x() /* foo */; !new x(); // foo !(x, y); !(x, y) /* foo */; -! /* foo */ (x, y); -! -/* foo */ -(x, y); -!(x, y) -/* foo */ -; +!(/* foo */ (x, y)); +!( + /* foo */ + (x, y) +); +!(x, y) /* foo */; !x.y; // foo !(() => 3); !(() => 3) /* foo */; -! /* foo */ (() => 3); -! -/* foo */ -(() => 3); -!(() => 3) -/* foo */ -; +!(/* foo */ (() => 3)); +!( + /* foo */ + (() => 3) +); +!(() => 3) /* foo */; !(() => 3); // foo function* bar() { !(yield x); !(yield x) /* foo */; - ! /* foo */ (yield x); - ! - /* foo */ - (yield x); - !(yield x) - /* foo */ - ; + !(/* foo */ (yield x)); + !( + /* foo */ + (yield x) + ); + !(yield x) /* foo */; !(yield x); // foo } async function bar2() { !(await x); !(await x) /* foo */; - ! /* foo */ (await x); - ! - /* foo */ - (await x); - !(await x) - /* foo */ - ; + !(/* foo */ (await x)); + !( + /* foo */ + (await x) + ); + !(await x) /* foo */; !(await x); // foo } ``` diff --git a/crates/rome_rowan/src/syntax/token.rs b/crates/rome_rowan/src/syntax/token.rs index 188aee52f17..9c266c40b5d 100644 --- a/crates/rome_rowan/src/syntax/token.rs +++ b/crates/rome_rowan/src/syntax/token.rs @@ -310,7 +310,9 @@ impl SyntaxToken { } } - /// Returns the token leading trivia. + /// Returns the token's leading trivia. + /// + /// Looking backward in the text, a token owns all of its preceding trivia up to and including the first newline character. /// /// ``` /// use rome_rowan::raw_language::{RawLanguage, RawLanguageKind, RawSyntaxTreeBuilder}; @@ -332,7 +334,9 @@ impl SyntaxToken { SyntaxTrivia::new(self.raw.leading_trivia()) } - /// Returns the token trailing trivia. + /// Returns the token's trailing trivia. + /// + /// A token owns all of its following trivia up to, but not including, the next newline character. /// /// ``` /// use rome_rowan::raw_language::{RawLanguage, RawLanguageKind, RawSyntaxTreeBuilder}; diff --git a/crates/rome_service/src/file_handlers/javascript.rs b/crates/rome_service/src/file_handlers/javascript.rs index 71a265adfb1..a4f3315fdae 100644 --- a/crates/rome_service/src/file_handlers/javascript.rs +++ b/crates/rome_service/src/file_handlers/javascript.rs @@ -4,8 +4,8 @@ use rome_formatter::{FormatError, Printed}; use rome_fs::RomePath; use rome_js_analyze::utils::rename::RenameError; use rome_js_analyze::{analyze, analyze_with_inspect_matcher, metadata, RuleError}; -use rome_js_formatter::context::{JsFormatOptions, QuoteProperties, QuoteStyle}; -use rome_js_formatter::format_node; +use rome_js_formatter::context::{QuoteProperties, QuoteStyle}; +use rome_js_formatter::{context::JsFormatOptions, format_node}; use rome_js_parser::Parse; use rome_js_semantic::semantic_model; use rome_js_syntax::{ diff --git a/website/playground/package.json b/website/playground/package.json index 75385c243f6..2ba100754a9 100644 --- a/website/playground/package.json +++ b/website/playground/package.json @@ -45,4 +45,4 @@ "engines": { "pnpm": "^7.0.0" } -} \ No newline at end of file +}