Skip to content

Commit 392adbf

Browse files
committed
Preserve prefixes outside line ranges
1 parent 6325332 commit 392adbf

7 files changed

Lines changed: 79 additions & 4 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
`from x import ( # fmt: skip`) when a standalone comment is among the bracket's
2828
contents: the whole statement is now preserved instead of being reformatted (and
2929
previously crashing) (#5161)
30+
- Preserve comments and blank lines outside requested ranges when formatting with
31+
`--line-ranges` (#5175)
3032

3133
### Preview style
3234

src/black/comments.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ class ProtoComment:
5555
consumed: int # how many characters of the original leaf's prefix did we consume
5656
form_feed: bool # is there a form feed before the comment
5757
leading_whitespace: str # leading whitespace before the comment, if any
58+
original_value: str # original comment text, before normalization
5859

5960

60-
def generate_comments(leaf: LN, mode: Mode) -> Iterator[Leaf]:
61+
def generate_comments(
62+
leaf: LN, mode: Mode, *, preserve_comment_formatting: bool = False
63+
) -> Iterator[Leaf]:
6164
"""Clean the prefix of the `leaf` and generate comments from it, if any.
6265
6366
Comments in lib2to3 are shoved into the whitespace prefix. This happens
@@ -82,7 +85,10 @@ def generate_comments(leaf: LN, mode: Mode) -> Iterator[Leaf]:
8285
):
8386
total_consumed = pc.consumed
8487
prefix = make_simple_prefix(pc.newlines, pc.form_feed)
85-
yield Leaf(pc.type, pc.value, prefix=prefix)
88+
value = pc.value
89+
if preserve_comment_formatting:
90+
value = pc.leading_whitespace + pc.original_value
91+
yield Leaf(pc.type, value, prefix=prefix)
8692
normalize_trailing_prefix(leaf, total_consumed)
8793

8894

@@ -127,6 +133,7 @@ def list_comments(prefix: str, *, is_endmarker: bool, mode: Mode) -> list[ProtoC
127133
consumed=consumed,
128134
form_feed=form_feed,
129135
leading_whitespace=whitespace,
136+
original_value=line.rstrip(),
130137
)
131138
)
132139
form_feed = False

src/black/linegen.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,20 @@ def visit_default(self, node: LN) -> Iterator[Line]:
148148
"""Default `visit_*()` implementation. Recurses to children of `node`."""
149149
if isinstance(node, Leaf):
150150
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
151-
for comment in generate_comments(node, mode=self.mode):
151+
preserve_comment_formatting = (
152+
node.type == STANDALONE_COMMENT
153+
and node.fmt_pass_converted_first_leaf is not None
154+
and node.line_ranges_converted
155+
)
156+
for comment in generate_comments(
157+
node,
158+
mode=self.mode,
159+
preserve_comment_formatting=preserve_comment_formatting,
160+
):
161+
if preserve_comment_formatting:
162+
indent = " " * self.current_line.depth
163+
if indent and comment.value.startswith(indent):
164+
comment.value = comment.value[len(indent) :]
152165
if any_open_brackets:
153166
# any comment within brackets is subject to splitting
154167
self.current_line.append(comment)

src/black/lines.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,8 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]:
981981
# Consume the first leaf's extra newlines.
982982
first_leaf = current_line.leaves[0]
983983
before = first_leaf.prefix.count("\n")
984-
before = min(before, max_allowed)
984+
if not current_line.is_fmt_pass_converted():
985+
before = min(before, max_allowed)
985986
first_leaf.prefix = ""
986987
else:
987988
before = 0

src/black/ranges.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ def _convert_node_to_standalone_comment(node: LN) -> None:
362362
value,
363363
prefix=prefix,
364364
fmt_pass_converted_first_leaf=first,
365+
line_ranges_converted=True,
365366
),
366367
)
367368

@@ -392,6 +393,7 @@ def _convert_nodes_to_standalone_comment(nodes: Sequence[LN], *, newline: Leaf)
392393
value,
393394
prefix=prefix,
394395
fmt_pass_converted_first_leaf=first,
396+
line_ranges_converted=True,
395397
),
396398
)
397399

src/blib2to3/pytree.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ class Leaf(Base):
381381
# code, and `fmt_pass_converted_first_leaf` points to the first Leaf in the
382382
# converted code.
383383
fmt_pass_converted_first_leaf: Optional["Leaf"] = None
384+
# True when this Leaf is converted from unchanged code for --line-ranges.
385+
line_ranges_converted: bool = False
384386

385387
def __init__(
386388
self,
@@ -391,6 +393,7 @@ def __init__(
391393
fixers_applied: list[Any] = [],
392394
opening_bracket: Optional["Leaf"] = None,
393395
fmt_pass_converted_first_leaf: Optional["Leaf"] = None,
396+
line_ranges_converted: bool = False,
394397
) -> None:
395398
"""
396399
Initializer.
@@ -410,6 +413,7 @@ def __init__(
410413
self.children = []
411414
self.opening_bracket = opening_bracket
412415
self.fmt_pass_converted_first_leaf = fmt_pass_converted_first_leaf
416+
self.line_ranges_converted = line_ranges_converted
413417

414418
def __repr__(self) -> str:
415419
"""Return a canonical string representation."""
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# flags: --line-ranges=2-10
2+
print("format me" )
3+
# format whitespace
4+
# format whitespace
5+
# format whitespace
6+
print( "format me" )
7+
# format whitespace
8+
# format whitespace
9+
# format whitespace
10+
print( "format me")
11+
12+
13+
14+
print("don't format me" )
15+
# don't format whitespace
16+
# don't format whitespace
17+
# don't format whitespace
18+
print( "don't format me" )
19+
# don't format whitespace
20+
# don't format whitespace
21+
# don't format whitespace
22+
print( "don't format me")
23+
24+
# output
25+
# flags: --line-ranges=2-10
26+
print("format me")
27+
# format whitespace
28+
# format whitespace
29+
# format whitespace
30+
print("format me")
31+
# format whitespace
32+
# format whitespace
33+
# format whitespace
34+
print("format me")
35+
36+
37+
38+
print("don't format me" )
39+
# don't format whitespace
40+
# don't format whitespace
41+
# don't format whitespace
42+
print( "don't format me" )
43+
# don't format whitespace
44+
# don't format whitespace
45+
# don't format whitespace
46+
print( "don't format me")

0 commit comments

Comments
 (0)