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

refactor(formatter): Refactor try_print_flat to fits_on_line #2589

Merged
merged 4 commits into from
May 18, 2022

Conversation

MichaReiser
Copy link
Contributor

@MichaReiser MichaReiser commented May 17, 2022

This PR brings the Printer closer to Prettier's printer by introducing a fits_on_line function that measures if printing this element won't exceed the line width.

This is somewhat similar to the existing try_print_flat implementation but differs in the sense that it also supports
measuring content that isn't printed in flat mode.

This functionality will be needed to implement alternatives because
the alternatives picks the first variant that fits on the line but
an alternative itself doesn't have to be printed in flat mode.

The PR doesn't change the semantics of fits yet so that only fits if the group's content fit as well as the remaining content in the document before the first new line character.

This PR further introduces the concept of a GroupId and adds the ability to use if_group_breaks and if_group_fits_on_line with a specific group id. This is necessary because this PR aligns the fill_elements semantic with Prettier's by introducing an implicit group for each element. The problem of doing this is that the trailing comma insertion no longer worked because the element itself fits on the line (printed in flat mode) but the outer group of the array did break:

group([
  '[',
  indent([
    soft_line_break(),
    fill([
      [
        token('2222222'),
        if_group_breaks(',') // Doesn't get printed because the element fits
      ]
  ]),
  soft_line_break(),
])

@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 10:23 Inactive
// print the group in "flat" mode, otherwise continue in expanded mode

let flat_args = args.with_print_mode(PrintMode::Flat);
// TODO replace `ElementCallQueue::default` with the real queue to also measure the rest of the document
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll follow up on this TODO in #2579 Passing the rest of the document will make sure that e.g. the following snipped breaks if the ] doesn't fit on the line:

[a, b, c]

@github-actions
Copy link

Parser conformance results on ubuntu-latest

js/262

Test result main count This PR count Difference
Total 45878 45878 0
Passed 44938 44938 0
Failed 940 940 0
Panics 0 0 0
Coverage 97.95% 97.95% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 39 39 0
Passed 36 36 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.31% 92.31% 0.00%

ts/babel

Test result main count This PR count Difference
Total 588 588 0
Passed 519 519 0
Failed 69 69 0
Panics 0 0 0
Coverage 88.27% 88.27% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 16257 16257 0
Passed 12391 12391 0
Failed 3866 3866 0
Panics 0 0 0
Coverage 76.22% 76.22% 0.00%

@github-actions
Copy link

github-actions bot commented May 17, 2022

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented May 17, 2022

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 18b7dc1
Status: ✅  Deploy successful!
Preview URL: https://ca04230f.tools-8rn.pages.dev

View logs

@MichaReiser MichaReiser force-pushed the refactor/printer-try-flat-to-fits branch from b134521 to 7d07d34 Compare May 17, 2022 10:30
@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 10:30 Inactive
// print the group in "flat" mode, otherwise continue in expanded mode

let flat_args = args.with_print_mode(PrintMode::Flat);
// TODO replace `ElementCallQueue::default` with the real queue to also measure the rest of the document
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is part of #2579 Passing in the queue will make sure that [a, b, c] only fits if the ] also fits on the line even if it's outside of the group.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would still need to allocate a clone of the current queue and not use the printer queue itself, since fits_on_line is just doing a lookahead check but shouldn't be actually dequeuing later elements

Copy link
Contributor Author

@MichaReiser MichaReiser May 17, 2022

Choose a reason for hiding this comment

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

MeasureQueue only iterates over the rest element without dequing them (read only)

let next = match self.queue.dequeue() {
Some(call) => (call.element, call.args),
None => {
let rest_item = self.rest_queue.next()?;
(rest_item.element, rest_item.args)
}
};
Some(next)
}

}

pub fn with_incremented_indent(mut self) -> Self {
self.indent += 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some comments have moved in this PR because with_incremented_indent only incremented the indent but didn't copy over the other fields like hard_group.

This PR brings the Printer closer to Prettier's printer by introducing a `fits_on_line` function that measures if printing this element won't exceed the line width.

This is somewhat similar to the existing `try_print_flat` implementation but differs in that sense that it also supports
measuring content that isn't printed in flat mode.

This functionality will be needed to implement alternatives because
the alternatives picks the first variant that fits on the line but
an alternative itself doesn't have to be printed in flat mode.

This PR further introduces the concept of a `GroupId` and adds the ability to use `if_group_breaks` and `if_group_fits_on_line` with a specific group id. This is necessary because this PR aligns the `fill_elements` semantic with Prettier's by introducing an implicit group for each element. The problem of doing this is that the trailing comma insertion no longer worked because the element itself fits on the line (printed in flat mode) but the outer group of the array did break:

```
group([
  '[',
  indent([
    soft_line_break(),
    fill([
      [
        token('2222222'),
        if_group_breaks(',') // Doesn't get printed because the element fits
      ]
  ]),
  soft_line_break(),
])
```
@MichaReiser MichaReiser force-pushed the refactor/printer-try-flat-to-fits branch from 7d07d34 to f8ca589 Compare May 17, 2022 10:36
@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 10:36 Inactive
@MichaReiser MichaReiser marked this pull request as ready for review May 17, 2022 10:36
@MichaReiser MichaReiser mentioned this pull request May 17, 2022
10 tasks
@MichaReiser
Copy link
Contributor Author

!bench_formatter

/// * The first and second content fit on a single line. It prints the content and separator in flat mode.
/// * The first content fits on a single line, but the second doesn't. It prints the content in flat and the separator in expanded mode.
/// * Neither the first nor the second content fit on the line. It brings the first content and the separator in expanded mode.
fn print_fill(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@NicholasLYang it should be straightforward for you to change the fill semantics so that it supports custom separators. Something I understand you need for JSX. This is prettier's implementation

https://github.com/prettier/prettier/blob/bb130d2d6e6ea42629b17a57869f5e7e550df6fd/src/document/doc-printer.js#L415-L486

Make sure to also change the fits handling inside fits_on_line

@github-actions
Copy link

Formatter Benchmark Results

group                                    main                                   pr
-----                                    ----                                   --
formatter/checker.ts                     1.00    415.3±1.45ms     6.3 MB/sec    1.06    440.0±4.50ms     5.9 MB/sec
formatter/compiler.js                    1.00    249.7±1.05ms     4.2 MB/sec    1.11    276.4±5.35ms     3.8 MB/sec
formatter/d3.min.js                      1.00    192.4±2.98ms  1395.2 KB/sec    1.10    211.7±3.28ms  1267.8 KB/sec
formatter/dojo.js                        1.00     13.3±0.08ms     5.2 MB/sec    1.16     15.4±0.59ms     4.5 MB/sec
formatter/ios.d.ts                       1.00    307.4±0.71ms     6.1 MB/sec    1.02    313.3±0.71ms     6.0 MB/sec
formatter/jquery.min.js                  1.00     53.7±0.18ms  1574.5 KB/sec    1.09     58.8±0.44ms  1440.5 KB/sec
formatter/math.js                        1.00    385.5±1.11ms  1719.9 KB/sec    1.13   436.1±11.67ms  1520.5 KB/sec
formatter/parser.ts                      1.00      9.3±0.03ms     5.2 MB/sec    1.06      9.9±0.10ms     4.9 MB/sec
formatter/pixi.min.js                    1.00    217.4±0.64ms     2.0 MB/sec    1.08    233.8±2.39ms  1922.0 KB/sec
formatter/react-dom.production.min.js    1.00     65.1±0.40ms  1811.0 KB/sec    1.10     71.5±0.92ms  1647.7 KB/sec
formatter/react.production.min.js        1.00      3.1±0.01ms  2027.2 KB/sec    1.05      3.3±0.00ms  1930.9 KB/sec
formatter/router.ts                      1.00      6.8±0.03ms     9.0 MB/sec    1.04      7.1±0.01ms     8.6 MB/sec
formatter/tex-chtml-full.js              1.00    484.1±1.63ms  1927.4 KB/sec    1.30   627.1±57.03ms  1488.1 KB/sec
formatter/three.min.js                   1.00    247.4±2.98ms     2.4 MB/sec    1.07    264.2±1.34ms     2.2 MB/sec
formatter/typescript.js                  1.00   1590.7±4.46ms     6.0 MB/sec    1.06   1692.8±9.97ms     5.6 MB/sec
formatter/vue.global.prod.js             1.00     87.9±0.26ms  1403.2 KB/sec    1.09     95.7±1.96ms  1289.1 KB/sec

@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 12:34 Inactive
@MichaReiser
Copy link
Contributor Author

!bench_formatter

@github-actions
Copy link

Formatter Benchmark Results

group                                    main                                   pr
-----                                    ----                                   --
formatter/checker.ts                     1.00   500.0±11.31ms     5.2 MB/sec    1.01   506.2±12.59ms     5.1 MB/sec
formatter/compiler.js                    1.01    308.7±6.21ms     3.4 MB/sec    1.00   305.8±10.63ms     3.4 MB/sec
formatter/d3.min.js                      1.00    228.1±6.03ms  1176.6 KB/sec    1.04    237.1±8.08ms  1131.9 KB/sec
formatter/dojo.js                        1.00     16.5±0.77ms     4.2 MB/sec    1.02     16.8±0.79ms     4.1 MB/sec
formatter/ios.d.ts                       1.00   362.6±12.54ms     5.1 MB/sec    1.00    362.5±8.44ms     5.1 MB/sec
formatter/jquery.min.js                  1.00     63.7±2.51ms  1327.8 KB/sec    1.00     64.0±2.96ms  1323.0 KB/sec
formatter/math.js                        1.00   461.7±10.93ms  1436.2 KB/sec    1.01    466.7±8.98ms  1420.8 KB/sec
formatter/parser.ts                      1.00     11.4±0.44ms     4.2 MB/sec    1.00     11.4±0.43ms     4.2 MB/sec
formatter/pixi.min.js                    1.00    261.7±9.85ms  1717.6 KB/sec    1.02   267.9±11.41ms  1677.5 KB/sec
formatter/react-dom.production.min.js    1.00     78.6±2.94ms  1500.3 KB/sec    1.02     79.8±4.23ms  1477.4 KB/sec
formatter/react.production.min.js        1.00      3.9±0.19ms  1629.1 KB/sec    1.01      3.9±0.13ms  1617.1 KB/sec
formatter/router.ts                      1.00      8.2±0.29ms     7.4 MB/sec    1.02      8.4±0.32ms     7.3 MB/sec
formatter/tex-chtml-full.js              1.00   587.7±13.97ms  1587.8 KB/sec    1.00   590.5±10.45ms  1580.2 KB/sec
formatter/three.min.js                   1.00    295.6±7.68ms  2034.1 KB/sec    1.02   302.7±11.99ms  1985.8 KB/sec
formatter/typescript.js                  1.00  1908.1±28.63ms     5.0 MB/sec    1.00  1917.0±23.69ms     5.0 MB/sec
formatter/vue.global.prod.js             1.00    106.7±2.37ms  1156.5 KB/sec    1.00    106.5±6.13ms  1158.1 KB/sec

}

#[cfg(target_pointer_width = "64")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking the pointer width seems unnecessary, TextRange is internally represented as two u32 so it's size is constant for all platforms, so having this cfg just means the assert isn't included when we build for WASM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The macro is only defined in that configuration but I guess I can remove that restrictions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The checks don't seem to work for wasm. So I'll leave it as-is for now.

crates/rome_formatter/src/printer/mod.rs Outdated Show resolved Hide resolved
crates/rome_formatter/src/printer/mod.rs Outdated Show resolved Hide resolved
crates/rome_formatter/src/printer/mod.rs Outdated Show resolved Hide resolved
// print the group in "flat" mode, otherwise continue in expanded mode

let flat_args = args.with_print_mode(PrintMode::Flat);
// TODO replace `ElementCallQueue::default` with the real queue to also measure the rest of the document
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would still need to allocate a clone of the current queue and not use the printer queue itself, since fits_on_line is just doing a lookahead check but shouldn't be actually dequeuing later elements

crates/rome_formatter/src/format_element.rs Outdated Show resolved Hide resolved
@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 14:35 Inactive
@MichaReiser MichaReiser force-pushed the refactor/printer-try-flat-to-fits branch from 4caecca to a4f4ca6 Compare May 17, 2022 15:38
@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 15:38 Inactive
@MichaReiser MichaReiser requested a review from leops May 17, 2022 15:40
@MichaReiser
Copy link
Contributor Author

Addressed the review feedback:

  • The PrinterState now uses a Vec<Option<GroupId>>
  • Moved the creation of GroupIds to the `formatter so that the ids are scoped to a single document
  • Rewrote fill loop to use a for

@MichaReiser MichaReiser force-pushed the refactor/printer-try-flat-to-fits branch from a4f4ca6 to 18b7dc1 Compare May 17, 2022 15:49
@MichaReiser MichaReiser temporarily deployed to aws May 17, 2022 15:49 Inactive
@MichaReiser MichaReiser changed the title refactor(formatter): Refactor try_print_flat to fits_on_line refactor(formatter): Refactor try_print_flat to fits_on_line May 17, 2022
@MichaReiser MichaReiser merged commit 0e4c017 into main May 18, 2022
@MichaReiser MichaReiser deleted the refactor/printer-try-flat-to-fits branch May 18, 2022 08:23
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants