0
# Line Selection Algorithm
1
2
Line selection utilities for choosing between original and reformatted code chunks based on which lines were edited. This module implements the core logic for selective formatting that only applies changes to modified regions.
3
4
## Capabilities
5
6
### Chunk Selection
7
8
Functions for choosing original or reformatted chunks based on edit locations.
9
10
```python { .api }
11
def choose_lines(
12
black_chunks: Iterable[DiffChunk],
13
edit_linenums: List[int],
14
) -> Generator[str, None, None]:
15
"""
16
Choose formatted chunks for edited areas, original chunks for non-edited.
17
18
This is the core function that implements Darker's selective formatting logic.
19
For each chunk from the formatter diff, it decides whether to use the
20
original or reformatted version based on whether any edited lines
21
fall within that chunk.
22
23
Parameters:
24
- black_chunks: Chunks from formatter showing original vs reformatted content
25
- edit_linenums: List of line numbers that were edited since last commit
26
27
Yields:
28
Lines from either original or reformatted content based on edit locations
29
"""
30
```
31
32
### Internal Utilities
33
34
Helper functions used by the line selection algorithm.
35
36
```python { .api }
37
def _any_item_in_range(items: List[int], start: int, length: int) -> bool:
38
"""
39
Return True if any item falls inside the slice [start : start + length].
40
41
If length == 0, add one to make sure an edit at the position of an inserted
42
chunk causes the reformatted version to be chosen for that chunk.
43
44
Parameters:
45
- items: List of line numbers to check
46
- start: Start of the range to check
47
- length: Length of the range (0 means single position)
48
49
Returns:
50
True if any items fall within the specified range
51
"""
52
```
53
54
## Usage Examples
55
56
### Basic Line Selection
57
58
```python
59
from darker.chooser import choose_lines
60
from darkgraylib.utils import DiffChunk
61
62
# Example chunks from formatter output
63
chunks = [
64
DiffChunk(
65
1, # chunk starts on line 1 in original
66
['def hello():', ' pass'], # original lines
67
['def hello() -> None:', ' pass'] # reformatted lines
68
),
69
DiffChunk(
70
3, # chunk starts on line 3 in original
71
['print("world")'], # original lines
72
['print("world")'] # reformatted lines (no change)
73
)
74
]
75
76
# Lines that were edited since last commit
77
edited_lines = [1] # only line 1 was modified
78
79
# Choose lines based on edits
80
result_lines = list(choose_lines(chunks, edited_lines))
81
82
print("Resulting code:")
83
for line in result_lines:
84
print(line)
85
# Output will use reformatted version for chunk 1 (contains edit)
86
# and original version for chunk 3 (no edits)
87
```
88
89
### Integration with Git Diff
90
91
```python
92
from darker.chooser import choose_lines
93
from darker.git import git_get_modified_python_files, EditedLinenumsDiffer
94
from darker.diff import diff_and_get_opcodes, opcodes_to_chunks
95
from darkgraylib.git import RevisionRange
96
from darkgraylib.utils import TextDocument
97
from pathlib import Path
98
99
# Get files modified in git
100
revrange = RevisionRange.parse_with_common_ancestor("HEAD", ":WORKTREE:")
101
modified_files = git_get_modified_python_files(["src/"], revrange, Path.cwd())
102
103
for file_path in modified_files:
104
# Get edited line numbers from git
105
differ = EditedLinenumsDiffer(Path.cwd(), revrange)
106
107
with open(file_path) as f:
108
current_content = TextDocument.from_str(f.read())
109
110
edited_lines = differ.revision_vs_lines(
111
Path(file_path),
112
current_content,
113
context_lines=0
114
)
115
116
# Assume we have formatter output chunks (from Black, Ruff, etc.)
117
# formatter_chunks = get_formatter_chunks(file_path, current_content)
118
119
# Choose lines based on git edits
120
# selected_lines = list(choose_lines(formatter_chunks, edited_lines))
121
122
print(f"File {file_path} has edits on lines: {edited_lines}")
123
```
124
125
### Debugging Line Selection
126
127
```python
128
import logging
129
from darker.chooser import choose_lines
130
131
# Enable debug logging to see selection decisions
132
logging.basicConfig(level=logging.DEBUG)
133
134
# The choose_lines function will log its decisions:
135
# - "Found edits on line X" or "Found no edits on lines X-Y"
136
# - "Using N reformatted/original/unmodified lines at line X"
137
138
chunks = [...] # your diff chunks
139
edited_lines = [...] # your edited line numbers
140
141
result = list(choose_lines(chunks, edited_lines))
142
```