-
Notifications
You must be signed in to change notification settings - Fork 48
Fix cell formatting issues #754
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
base: main
Are you sure you want to change the base?
Conversation
8c9747e to
0902ef2
Compare
| edits.forEach((edit) => { | ||
| editBuilder.replace(edit.range, edit.newText); | ||
| }); | ||
| // Sort edits by descending start position to avoid range shifting issues |
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.
Unrelated but I thought we should make this robust to providers returning unsorted edits.
apps/vscode/src/providers/format.ts
Outdated
|
|
||
| // Bail if any edit is out of range. We used to filter these edits out but | ||
| // this could bork the cell. | ||
| if (edits.some(edit => !blockRange.contains(edit.range))) { |
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.
Unrelated but I'm pretty sure applying edits partially can bork our cells 😬
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 pulled down this PR, rebased on main, and followed the reproduction steps in #745 (which this PR should address). I ran into a new error that prevented any formatting from occuring:
I reverted this code change locally (bailing if edits are out of range) and formatting works again and the problem described in #745 is addressed.
|
Thanks for the PR! Something for us to consider: Every time I see a PR here I get a feeling of dread: we have no test coverage and so I have very little visibility or intuition of the impact that these changes make. What kind of infrastructure would we have to add for us to have unit test coverage for these kinds of changes? |
|
Part of the reason this is still a draft is that I'd like to add some tests here. They'll be limited in scope though. Here are the levels of tests we have in our R stack, by ascending level of scope:
We spend most of our time and energy on internal LSP tests. |
|
@lionel- we now have extension tests! The tests are focused on roundtripping right now, but they do already involve executing commands. Do you think an extension test (a snapshot test?) that opens our test qmd files, executes the format command, and checks the contents of the file would provide enough coverage for this PR? I'd like to get this PR in, so I'm happy to write the tests. Please advise! |
|
I pulled down this PR, rebased on main, and followed the reproduction steps in #745 (which this PR should address). I ran into a new error that prevented any formatting from occuring:
I was able to reproduce #745 in Positron on the |
|
I have some local changes where I'm able to load the minimal repro qmd specified in #745, set the editor's cursor to inside a code cell, and do
...but the cursor is definitely within a cell. I suspect that the Quarto extension is toasting this because there is no R formatter available. The test VSCode instance has 0 extensions installed: I know from looking under the extensions tab while the tests are running. That means there's no R extension. That means there's no R formatter available! It is confusing to me that there are 0 extensions installed because the vscode testing guide says:
but that does not seem to be the case for our extension tests. For one other thing, the test instance of VSCode always has an |
|
Another thought I had is that we might want to mock out a formatter to use in the tests, or maybe two (one that adds a final newline and one that does not). |
|
@juliasilge and I discussed a couple possible ways to test this PR:
|
|
I made a copy of this PR: #818, it is
Excuse me for copying the branch&PR to a new branch&PR, but I didn't feel comfortable force pushing (because of the rebase on main) to this one. I don't currently know how to interpret the test results I am getting in #818: The test does what I expect until I ran the test after reverting the main fix by @lionel- (the change in |
Sounds reasonable!
I like (1) as it reliably targets the behaviour we want to test. The second solution would make the snapshots dependent on external behaviour (formatters like ruff change behaviour over time). This might be reasonable for monitoring a file as a sanity check, but probably not as a basis for comprehensive testing. |
Do you mean that we should keep applying partial edits? I don't think we should do that ever because this runs the risk of corrupting the cell contents. To see this, imagine a formatter returning two edits, one that removes lines 4-6 with 6 being out of range, and another edit that changes line 4. If you discard the first edit, the second will change the wrong line. Relevant bit from the LSP protocol:
Note that this requires the sorting that I added in this PR to be stable, so that the order of edits starting at the same position is preserved, which is guaranteed by ES2019. (We should add a comment about that.) To be robust, which I think we should because of the potential of corrupting the user's work without them being aware, we should either:
I think the reason edits may be OOR is that So it should be safe to adjust OOR edits by substracting 1 to the high bound. If the edit is still OOR after that, then we probably should error out as the edit is not consistent with our assumptions about the document. @DavisVaughan Does this reasoning make sense to you? |
|
Thanks for the thorough comment @lionel-! Very clarifying.
I mostly did that just because I didn't understand your fix or what is going on in this area in general and was trying things to get it working. I understand now that the right thing to do should be to modify the out of range edits like you described. |
Maybe a better way to go about this is to:
This way we don't need to adjust out of range text edits. This approach feels cleaner to me. |
|
@lionel- for this particular Note that for something like this it will leave the trailing newlines due to your last bullet, which I don't love? Is it simpler to do this?
That way we don't need any fancy tracking, and we always clean up extraneous blank lines in a code chunk |
|
Hmm then maybe we should go all the way and trim all trailing newlines at the end of a chunk? Leading and trailing newlines in chunks might be considered part of quarto formatting rules, and I think we don't expect any surrounding whitespace in chunks? |
I think we are in agreement already. By "exactly 1 trailing newline", I just mean that you need a formats to So yea the code content itself becomes just |
|
oh gotcha, yep I'm all for that |
|
@vezwork Sounds good! |
0902ef2 to
186ac61
Compare
|
rebased on main and force pushed. For some reason I'm a co-author on the commits now. |
|
Added a test that currently fails on this branch because of "out of range edits" from the mock formatter. It will also fail if the |
873fcab to
87bfcc5
Compare
|
rebased on |
|
Coming back to this PR. I added a code change so I'm now trying to normalize the trailing newlines in the way that @DavisVaughan described:
I am having trouble doing the 2nd and 3rd bullet: In In other words, we have to return Still working through, just thought I'd drop my thoughts so far. |
|
I've decided the best thing to do is to try to land this PR without trailing newline normalization. I attempted to make a more direct test of the functionality of this PR: for the formatting of by logging out the edits that come from the R formatter for the virtual doc of this cell, then constructing a provider in the tests that is hard-coded to return those edits... but I realized this doesn't work because this PR fixes the virtual doc that is passed to the R formatter, resulting in it giving correct edits. So I don't think we can reasonably test that without having access to the actual R formatter, which is not reasonable to do. So, I am going to remove the test from this PR. Those tests would still be useful if we want to tackle normalizing trailing newlines, I put the test here #890 for later. I have tested locally in VSCode, and this PR now fixes all the issues listed in #745. It also works for Python cells. |
6738101 to
a98d4cc
Compare
|
Ok, the PR is ready for review. Summary of what you need to know to review:This PR doesn't have tests for the reason I described in the previous comment. This PR does not cause trailing newlines to be normalized when formatting cells (like a lot of the discussion in this PR was talking about). It is considered out of scope for this PR. It is left up to the language formatter. Tests for normalized trailing newlines during cell formatting were moved to #890. I manually tested that this PR fixes the issues in #754. The PR was almost completely written by @lionel-, though I caught a problem where we were checking if |
| .filter(edit => blockRange.contains(edit.range)); | ||
| }); | ||
|
|
||
| // Bail if any edit is out of range. We used to filter these edits out but | ||
| // this could bork the cell. | ||
| if (adjustedEdits.some(edit => !blockRange.contains(edit.range))) { | ||
| window.showInformationMessage( | ||
| "Formatting edits were out of range and could not be applied to the code cell." | ||
| ); | ||
| return []; | ||
| } |
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 does feel safer to me
juliasilge
left a comment
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'm supportive of us merging this because it gives us an improvement, but I want to highlight a difference in results between very similar formatters, when you use Format Document.
If you have settings like this:
{
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
},
"[r]": {
"editor.defaultFormatter": "Posit.air-vscode"
},
"[quarto]": {
"editor.defaultFormatter": "quarto.quarto"
}
}And two .qmd files like this:
---
title: "Untitled"
format: html
---
```{python}
1 + 1
```
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
```{python}
from typing import Iterable
import os
def sum_even_numbers(numbers: Iterable[int]) -> int:
"""Given an iterable of integers, return the sum of all even numbers in the iterable."""
return sum(
num for num in numbers
if num % 2 == 0
)
```
Second one:
---
title: "Untitled"
format: html
---
```{r}
1 + 1
```
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
```{r}
list(foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar)
```
- Put your cursor in the
1 + 1code cell - Run Format Document
- In the R file, the other code cell will get formatted, which is what I would argue is the correct behavior
- In the Python file, the other code cell not get formatted, which I think is not right
If you run Format Document from the code cell that needs formatting, the behavior is the same for both; it does format that code cell. The command Quarto: Format Cell also works well for both files.
We can log this problem to follow up on.
|
Can you update the CHANGELOG before merging, @vezwork? |


Addresses #745
But needs to be tested more widely and deal with formatters that don't ensure a trailing newline as discussed in #745 (comment).