Skip to content

Commit 0ce5aba

Browse files
committed
Add --diff option to show diff when running with --check
1 parent 0cbd205 commit 0ce5aba

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

src/mdformat/_cli.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import argparse
44
from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
55
import contextlib
6+
from datetime import datetime
67
import itertools
78
import logging
89
from pathlib import Path
@@ -17,6 +18,7 @@
1718
from mdformat._util import atomic_write, detect_newline_type, is_md_equal
1819
import mdformat.plugins
1920
import mdformat.renderer
21+
from src.mdformat._output import diff
2022

2123
# Match "\r" and "\n" characters that are not part of a "\r\n" sequence
2224
RE_NON_CRLF_LINE_END = re.compile(r"(?:[^\r]|^)\n|\r(?:[^\n]|\Z)")
@@ -92,6 +94,17 @@ def run(cli_args: Sequence[str]) -> int: # noqa: C901
9294
):
9395
format_errors_found = True
9496
print_error(f'File "{path_str}" is not formatted.')
97+
98+
if opts["diff"]:
99+
then = datetime.utcfromtimestamp(path.stat().st_mtime)
100+
now = datetime.utcnow()
101+
src_name = f"{path}\t{then} +0000"
102+
dst_name = f"{path}\t{now} +0000"
103+
104+
diff_contents = diff(
105+
original_str, formatted_str, src_name, dst_name
106+
)
107+
print(diff_contents)
95108
else:
96109
if not changes_ast and not is_md_equal(
97110
original_str,
@@ -146,6 +159,11 @@ def make_arg_parser(
146159
parser.add_argument(
147160
"--check", action="store_true", help="do not apply changes to files"
148161
)
162+
parser.add_argument(
163+
"--diff",
164+
action="store_true",
165+
help="show diff of what would be changed when running with --check",
166+
)
149167
version_str = f"mdformat {mdformat.__version__}"
150168
if plugin_versions_str:
151169
version_str += f" ({plugin_versions_str})"

src/mdformat/_output.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import difflib
2+
3+
4+
def diff(a: str, b: str, a_name: str, b_name: str) -> str:
5+
"""Return a unified diff string between strings `a` and `b`.
6+
7+
Highly inspired by Black's diff function.
8+
"""
9+
a_lines = a.splitlines(keepends=True)
10+
b_lines = b.splitlines(keepends=True)
11+
12+
diff_lines = []
13+
for line in difflib.unified_diff(
14+
a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5
15+
):
16+
if line[-1] == "\n":
17+
diff_lines.append(line)
18+
else:
19+
diff_lines.append(line + "\n")
20+
diff_lines.append("\\ No newline at end of file\n")
21+
22+
return "".join(diff_lines)

tests/test_cli.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ def test_check__fail(tmp_path):
8282
assert run((str(file_path), "--check")) == 1
8383

8484

85+
def test_check_fail_diff(capsys, tmp_path):
86+
"""Test for --check flag and --diff flag combined on unformatted files.
87+
88+
Test that when an unformatted file fails, a diff is writtin to
89+
stdout.
90+
"""
91+
92+
file_path = tmp_path / "test_markdown.md"
93+
file_path.write_text(UNFORMATTED_MARKDOWN)
94+
assert run((str(file_path), "--check", "--diff")) == 1
95+
captured = capsys.readouterr()
96+
assert str(file_path) in captured.out
97+
assert "-\n-\n # A header\n-\n" in captured.out
98+
99+
85100
def test_check__multi_fail(capsys, tmp_path):
86101
"""Test for --check flag when multiple files are unformatted.
87102

0 commit comments

Comments
 (0)