Skip to content
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

Add --diff option ruff format #7937

Merged
merged 10 commits into from
Oct 18, 2023
Merged

Add --diff option ruff format #7937

merged 10 commits into from
Oct 18, 2023

Conversation

konstin
Copy link
Member

@konstin konstin commented Oct 13, 2023

Summary ruff format --diff is similar to ruff format --check, but we don't only error with the list of file that would be formatted, but also show a diff between the unformatted input and the formatted output.

$ ruff format --diff scratch.py scratch.pyi scratch.ipynb
warning: `ruff format` is not yet stable, and subject to change in future versions.
--- scratch.ipynb
+++ scratch.ipynb
@@ -1,3 +1,4 @@
 import numpy
-maths = (numpy.arange(100)**2).sum()
-stats= numpy.asarray([1,2,3,4]).median()
+
+maths = (numpy.arange(100) ** 2).sum()
+stats = numpy.asarray([1, 2, 3, 4]).median()
--- scratch.py
+++ scratch.py
@@ -1,3 +1,3 @@
 x = 1
-y=2
+y = 2
 z = 3
2 files would be reformatted, 1 file left unchanged

With --diff, the summary message gets printed to stderr to allow e.g. ruff format --diff . > format.patch.

At the moment, jupyter notebooks are formatted as code diffs, while everything else is a real diff that could be applied. This means that the diffs containing jupyter notebooks are not real diffs and can't be applied. We could change this to json diffs, but they are hard to read. We could also split the diff option into a human diff option, where we deviate from the machine readable diff constraints, and a proper machine readable, appliable diff output that you can pipe into other tools.

To make the tests work, the results (and errors, if any) are sorted before printing them. Previously, the print order was random, i.e. two identical runs could have different output.

Open question: Should this go into the markdown docs? Or will this be subsumed by the integration of the formatter into ruff check?

Test plan Fixtures for the change and no change cases, including a jupyter notebook and for file input and stdin.

Fixes #7231

@konstin konstin added the formatter Related to the formatter label Oct 13, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Oct 13, 2023

PR Check Results

Ecosystem

✅ ecosystem check detected no changes.

Formatted {
unformatted: SourceKind,
formatted: SourceKind,
},
Copy link
Member

Choose a reason for hiding this comment

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

This is making me wonder if format_source should take &SourceKind, and we should change Unchanged(SourceKind) to just Unchanged, and remove unformatted from the Formatted variant. It strikes me as inflexible that the method takes ownership of SourceKind but only acts on references and then returns the owned SourceKind in all cases. But, this is minor -- if it doesn't work for whatever reason, or you disagree, it's fine.

Copy link
Member

Choose a reason for hiding this comment

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

I guess the challenge with this is that we need to move SourceKind out of the linter, maybe into ruff_source_file? I recommend investigating this as a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thought so too but didn't want to create so much churn, changed it now

@charliermarsh
Copy link
Member

Should this go into the markdown docs?

I think it's fine to leave this as-is for now -- I will fold into the documentation when I revisit it next week.

@MichaReiser MichaReiser added the cli Related to the command-line interface label Oct 16, 2023
crates/ruff_cli/src/commands/format.rs Outdated Show resolved Hide resolved
crates/ruff_cli/src/commands/format.rs Outdated Show resolved Hide resolved
Formatted {
unformatted: SourceKind,
formatted: SourceKind,
},
Copy link
Member

Choose a reason for hiding this comment

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

I guess the challenge with this is that we need to move SourceKind out of the linter, maybe into ruff_source_file? I recommend investigating this as a separate PR.

crates/ruff_cli/src/commands/format.rs Outdated Show resolved Hide resolved
Comment on lines 408 to 426
let mut writer = stdout().lock();
let text_diff = TextDiff::from_lines(
unformatted.source_code(),
formatted_source.source_code(),
);
let mut unified_diff = text_diff.unified_diff();
unified_diff.header(&fs::relativize_path(path), &fs::relativize_path(path));
unified_diff.to_writer(&mut writer).unwrap();

formatted += 1;
}
Copy link
Member

Choose a reason for hiding this comment

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

Nit: It feels a bit strange that the diff is written as part of the FormatSummary's Display implementation (the diff isn't part of the summary).

I would prefer moving the diff printing out of the summary implementation and handle it explicitly when the FormatMode is Diff (also allows to locking stdout exactly once)

Copy link
Member Author

Choose a reason for hiding this comment

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

I went with renaming FormatSummary to FormatResults and giving it two different write methods. We unfortunately lose the fmt impl but we get proper separation

@konstin
Copy link
Member Author

konstin commented Oct 16, 2023

I guess the challenge with this is that we need to move SourceKind out of the linter, maybe into ruff_source_file? I recommend investigating this as a separate PR.

Did work without it, but i'm fine with moving it anyway

konstin and others added 9 commits October 18, 2023 13:40
**Summary** `ruff format --diff` is similar to `ruff format --check`, but we don't only error with the list of file that would be formatted, but also show a diff between the unformatted input and the formatted output.

```console
$ ruff format --diff scratch.py scratch.pyi scratch.ipynb
warning: `ruff format` is not yet stable, and subject to change in future versions.
--- scratch.ipynb
+++ scratch.ipynb
@@ -1,3 +1,4 @@
 import numpy
-maths = (numpy.arange(100)**2).sum()
-stats= numpy.asarray([1,2,3,4]).median()
+
+maths = (numpy.arange(100) ** 2).sum()
+stats = numpy.asarray([1, 2, 3, 4]).median()
--- scratch.py
+++ scratch.py
@@ -1,3 +1,3 @@
 x = 1
-y=2
+y = 2
 z = 3
2 files would be reformatted, 1 file left unchanged
```

With `--diff`, the summary message gets printed to stderr to allow e.g. `ruff format --diff . > format.patch`.

At the moment, jupyter notebooks are formatted as code diffs, while everything else is a real diff that could be applied. This means that the diffs containing jupyter notebooks are not real diffs and can't be applied. We could change this to json diffs, but they are hard to read. We could also split the diff option into a human diff option, where we deviate from the machine readable diff constraints, and a proper machine readable, appliable diff output that you can pipe into other tools.

**Test plan** Fixtures for the change and no change cases, including a jupyter notebook and for file input and stdin.
@konstin konstin enabled auto-merge (squash) October 18, 2023 11:45
@konstin konstin merged commit 51aa73f into main Oct 18, 2023
15 checks passed
@konstin konstin deleted the format-diff branch October 18, 2023 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cli Related to the command-line interface formatter Related to the formatter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Formatter: Add an option to show formatting changes when running --check
3 participants