-
-
Notifications
You must be signed in to change notification settings - Fork 499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(js_formatter): Use fluid assignment layout when left side is breakable #1021
Conversation
✅ Deploy Preview for rad-torte-839a59 ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
pub const fn is_line(&self) -> bool { | ||
matches!(self, FormatElement::Line(_)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda surprised this didn't exist before. Turns out it's not actually necessary because may_directly_break
does this check itself, but I figure this makes sense to keep around anyway.
match element { | ||
// Line suffix | ||
// Ignore if any of its content breaks | ||
FormatElement::Tag(StartLineSuffix) => { | ||
ignore_depth += 1; | ||
} | ||
FormatElement::Tag(EndLineSuffix) => { | ||
ignore_depth -= 1; | ||
} | ||
FormatElement::Interned(interned) if ignore_depth == 0 => { | ||
if interned.may_directly_break() { | ||
return true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to not duplicate this code again, but idk a cleaner way to write it out. I think this state management is still necessary for this function, since it also shouldn't be inspecting text content on suffixes, but I'm not 100% sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine :) the duplicated code isn't that much
if self.list.is_empty() { | ||
return Ok(()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning early here is important, because otherwise this node returns a group that always has a soft_line_break_or_space
, which causes may_directly_break
to return true. An example case this affects is:
class Test {
prop1 = // comment
true;
}
Without this early escape, the IR gets written as:
group(expand: propagated, [
group(expand: true, [soft_line_break_or_space]),
" prop1",
line_suffix([" // comment"]),
expand_parent
]),
" = true"
and that group with just the soft line is this modifier list. With this early return, that group no longer exists, meaning the outer group won't contain any potential breaks, and the comments get formatted as expected by Prettier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great comment, and a I think we should have it inside the code :) (maybe a variation of that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to merge it once we add code comment that explains the empty list :)
/// such as when the group contains a [crate::builders::expand_parent] or some text within the group | ||
/// contains a newline. Neither of those cases directly contain a [FormatElement::Line], and so they | ||
/// do not _directly_ break. | ||
fn may_directly_break(&self) -> bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind the name of the function, but I wonder if at this point, we should rename will_break
in will_inderectly_break
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I don't know which is the better name between will_break
, will_directly_break
or will_indirectly_break
...because they're all both accurate and inaccurate in different ways lol. indirectly
i think implies that the element won't break itself, but some child element does, or something like that. directly
implies that the element itself does, but children might also break instead. the plain will_break
at least covers both of those, despite being a little more ambiguous to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I hope to find better names , because the current ones aren't very good and the prettier code base doesn't help
match element { | ||
// Line suffix | ||
// Ignore if any of its content breaks | ||
FormatElement::Tag(StartLineSuffix) => { | ||
ignore_depth += 1; | ||
} | ||
FormatElement::Tag(EndLineSuffix) => { | ||
ignore_depth -= 1; | ||
} | ||
FormatElement::Interned(interned) if ignore_depth == 0 => { | ||
if interned.may_directly_break() { | ||
return true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine :) the duplicated code isn't that much
if self.list.is_empty() { | ||
return Ok(()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great comment, and a I think we should have it inside the code :) (maybe a variation of that)
Summary
This is an equivalent fix for prettier/prettier#15534, where an assignment with a left-hand side that is breakable inadvertently used the
NeverBreakAfterOperator
assignment layout, causing it to break before the operator would: Playground Link.Implementing this required a few additions to match some utilities that Prettier uses, specifically a new
may_directly_break
function to complement the existingwill_break
. Wherewill_break
checks if an element or list of elements is guaranteed to break because of its content, the newmay_directly_break
checks if an element could potentially break, by way of containing aLine
element of any kind. The main consideration here is thatwill_break
only considers hard lines, whilemay_directly_break
also accounts for soft lines.The function is...a little bit strange semantically, just because it's not quite the same type of check as
will_break
, so I'm not super confident about the naming of it, but it matchescan_break
in Prettier, which feels close enough I guess.There are a few other oddities throughout that I'll add comments on directly.
Test Plan
The prettier diff snapshot is deleted because they now match, and no other tests have been affected.