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

feat(rome_js_formatter): Format Member Chain #3273

Merged
merged 4 commits into from
Sep 28, 2022

Conversation

MichaReiser
Copy link
Contributor

@MichaReiser MichaReiser commented Sep 26, 2022

Summary

This PR implements member chain formatting the same way as prettier does.

There are still some differences in combination with call arguments because our call arguments formatting doesn't match Prettier's yet. I plan to tackle this as a separate PR.

Test Plan

Average compatibility: 88.42 -> 89.48%
Compatible lines: 89.3 -> 90.47%

@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 09:47 Inactive
@netlify
Copy link

netlify bot commented Sep 26, 2022

Deploy Preview for rometools canceled.

Name Link
🔨 Latest commit 3dc58c3
🔍 Latest deploy log https://app.netlify.com/sites/rometools/deploys/6332e75e73afce0008ac000d

@github-actions
Copy link

github-actions bot commented Sep 26, 2022

@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:16 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:18 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:21 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:21 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:21 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:22 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 26, 2022 10:25 Inactive
@MichaReiser
Copy link
Contributor Author

!bench_formatter

@github-actions
Copy link

Formatter Benchmark Results

group                                    main                                   pr
-----                                    ----                                   --
formatter/checker.ts                     1.01   544.9±14.48ms     4.8 MB/sec    1.00   540.3±17.06ms     4.8 MB/sec
formatter/compiler.js                    1.00   297.9±10.11ms     3.5 MB/sec    1.00    297.9±9.66ms     3.5 MB/sec
formatter/d3.min.js                      1.00    221.7±8.63ms  1210.5 KB/sec    1.04    229.7±6.56ms  1168.6 KB/sec
formatter/dojo.js                        1.00     13.8±0.51ms     5.0 MB/sec    1.00     13.8±0.60ms     5.0 MB/sec
formatter/ios.d.ts                       1.00    322.4±9.51ms     5.8 MB/sec    1.00   323.3±10.31ms     5.8 MB/sec
formatter/jquery.min.js                  1.00     60.3±2.97ms  1404.5 KB/sec    1.00     60.4±2.65ms  1402.0 KB/sec
formatter/math.js                        1.00   446.2±15.25ms  1486.1 KB/sec    1.00   446.2±10.57ms  1486.1 KB/sec
formatter/parser.ts                      1.00      9.5±0.51ms     5.1 MB/sec    1.03      9.8±0.36ms     5.0 MB/sec
formatter/pixi.min.js                    1.00    247.1±7.12ms  1818.9 KB/sec    1.00    247.9±9.12ms  1812.9 KB/sec
formatter/react-dom.production.min.js    1.00     73.4±3.02ms  1604.9 KB/sec    1.06     78.2±3.57ms  1507.8 KB/sec
formatter/react.production.min.js        1.00      3.3±0.20ms  1896.1 KB/sec    1.01      3.3±0.18ms  1881.2 KB/sec
formatter/router.ts                      1.00      7.7±0.36ms     8.0 MB/sec    1.05      8.0±0.28ms     7.7 MB/sec
formatter/tex-chtml-full.js              1.00   562.3±16.06ms  1659.6 KB/sec    1.05   587.8±21.74ms  1587.6 KB/sec
formatter/three.min.js                   1.00    281.7±9.50ms     2.1 MB/sec    1.04    292.5±8.84ms     2.0 MB/sec
formatter/typescript.js                  1.00  1906.9±44.33ms     5.0 MB/sec    1.02  1950.0±46.68ms     4.9 MB/sec
formatter/vue.global.prod.js             1.00     94.4±3.85ms  1306.3 KB/sec    1.08    102.0±7.88ms  1209.6 KB/sec

@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 06:48 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 07:21 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 07:26 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 07:42 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 10:02 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 10:14 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 10:22 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 10:23 Inactive
@MichaReiser MichaReiser temporarily deployed to netlify-playground September 27, 2022 12:06 Inactive
@MichaReiser MichaReiser changed the base branch from main to refactor/best-fitting-fits September 27, 2022 12:07
@MichaReiser MichaReiser added the A-Formatter Area: formatter label Sep 27, 2022
@MichaReiser MichaReiser added this to the 0.10.0 milestone Sep 27, 2022
@MichaReiser MichaReiser marked this pull request as ready for review September 27, 2022 12:53
@MichaReiser MichaReiser requested a review from a team September 27, 2022 12:53
@MichaReiser
Copy link
Contributor Author

!bench_formatter

@github-actions
Copy link

Formatter Benchmark Results

group                                    main                                   pr
-----                                    ----                                   --
formatter/checker.ts                     1.00    388.5±1.50ms     6.7 MB/sec    1.01    393.2±1.29ms     6.6 MB/sec
formatter/compiler.js                    1.00    213.7±1.08ms     4.9 MB/sec    1.05    223.3±1.62ms     4.7 MB/sec
formatter/d3.min.js                      1.00    170.9±0.55ms  1570.7 KB/sec    1.03    175.3±0.92ms  1530.8 KB/sec
formatter/dojo.js                        1.00     11.4±0.07ms     6.0 MB/sec    1.02     11.7±0.05ms     5.9 MB/sec
formatter/ios.d.ts                       1.00    248.2±0.75ms     7.5 MB/sec    1.02    252.4±1.31ms     7.4 MB/sec
formatter/jquery.min.js                  1.00     46.9±0.22ms  1804.1 KB/sec    1.03     48.5±0.22ms  1745.9 KB/sec
formatter/math.js                        1.00    334.4±3.15ms  1983.1 KB/sec    1.01    339.3±2.18ms  1954.2 KB/sec
formatter/parser.ts                      1.00      7.7±0.05ms     6.3 MB/sec    1.05      8.1±0.03ms     6.0 MB/sec
formatter/pixi.min.js                    1.00    185.7±0.99ms     2.4 MB/sec    1.04    192.6±0.82ms     2.3 MB/sec
formatter/react-dom.production.min.js    1.00     57.4±0.45ms     2.0 MB/sec    1.02     58.8±0.65ms  2004.8 KB/sec
formatter/react.production.min.js        1.00      2.7±0.02ms     2.3 MB/sec    1.03      2.8±0.02ms     2.2 MB/sec
formatter/router.ts                      1.00      6.3±0.04ms     9.7 MB/sec    1.04      6.6±0.05ms     9.3 MB/sec
formatter/tex-chtml-full.js              1.00    426.6±1.07ms     2.1 MB/sec    1.04    443.5±1.22ms     2.1 MB/sec
formatter/three.min.js                   1.00    218.6±0.85ms     2.7 MB/sec    1.05    230.3±1.39ms     2.5 MB/sec
formatter/typescript.js                  1.00   1400.9±5.48ms     6.8 MB/sec    1.04   1451.5±5.06ms     6.5 MB/sec
formatter/vue.global.prod.js             1.00     73.3±0.47ms  1683.3 KB/sec    1.03     75.2±0.52ms  1639.7 KB/sec

for (index, argument) in separated.iter_mut().enumerate() {
let breaks = argument.inspect(f)?.will_break();

any_argument_breaks = any_argument_breaks || breaks;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
any_argument_breaks = any_argument_breaks || breaks;
any_argument_breaks |= breaks;

Copy link
Contributor Author

@MichaReiser MichaReiser Sep 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the same to my understanding because || is the logical or whereas |= does a bitwise or. The logical or allows to short circuit the right hand side if the left side is already true which isn't the case for bitwise or.

rust operators

if (should_group_first_argument && index > 0)
|| (should_group_last_argument && index < args.len() - 1)
{
first_last_breaks = first_last_breaks || breaks;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
first_last_breaks = first_last_breaks || breaks;
first_last_breaks |= breaks;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. |= performs a bitwise or where || performance a logical or.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do the same thing in another part of the code, why here is different? https://github.com/rome/tools/blob/main/crates/rome_cli/src/traversal.rs#L356

Copy link
Contributor Author

@MichaReiser MichaReiser Sep 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's important to be different here because it allows the compiler to short circuit to not compute breaks.

I don't know if this is something we want (or don't want) in the link you shared but I would be careful about using bitwise operations (it's most often not what you want).

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this example:


use ::std::*;

fn some_function(x: usize) -> bool {
    x % 2 == 0
}


pub fn main ()
{


    let mut ord = false;
    
    let mut bitwise_or = false;
    
    for x in 0..10 {
        bitwise_or |= some_function(x);
        ord = ord || some_function(x);
    }
    
    dbg!(ord);
    dbg!(bitwise_or);
}

Rust generates very different code for |= and ||

for |=

        call    example::some_function
        mov     byte ptr [rsp + 159], al
        mov     cl, byte ptr [rsp + 159]
        mov     al, byte ptr [rsp + 199]
        or      al, cl
        and     al, 1
        mov     byte ptr [rsp + 199], al

You can see how it unconditionally calls some_function and then ors the result

for ||

        test    byte ptr [rsp + 198], 1
        jne     .LBB20_9
        mov     rdi, qword ptr [rsp + 144]
        call    example::some_function
        mov     byte ptr [rsp + 143], al
        jmp     .LBB20_11
.LBB20_9:
        mov     byte ptr [rsp + 254], 1
.LBB20_10:
        mov     al, byte ptr [rsp + 254]
        and     al, 1
        mov     byte ptr [rsp + 198], al
        jmp     .LBB20_2
.LBB20_11:
        mov     al, byte ptr [rsp + 143]
        and     al, 1
        mov     byte ptr [rsp + 254], al
        jmp     .LBB20_10

It first tests if ord is true. If so, it jumps to the block LBB20_9 (not calling some_function). It only calls into some_function if ord is false.

LLVM may still be able to optimise to the same output BUT using | has stricter calling conventions than || where it's OK to not compute the right value if the left value is true.

compiler explorer

Whether the use of |= is correct in the other places is probably best answered by @leops who wrote the code (in some places it certainly is because it uses bitflags. In some cases it isn't clear to me because left and right are booleans and maybe using || would have been more appropriate).

crates/rome_js_formatter/src/utils/member_chain/mod.rs Outdated Show resolved Hide resolved
@MichaReiser MichaReiser merged commit f604c39 into refactor/best-fitting-fits Sep 28, 2022
@MichaReiser MichaReiser deleted the feat/member-chain branch September 28, 2022 15:13
@MichaReiser MichaReiser mentioned this pull request Sep 29, 2022
9 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-Formatter Area: formatter
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants