Skip to content

Commit df30aaf

Browse files
adjust docs processor
1 parent ed1a1c6 commit df30aaf

File tree

1 file changed

+174
-69
lines changed

1 file changed

+174
-69
lines changed

docs/processor.py

Lines changed: 174 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,21 @@
1313

1414
import logging
1515
import re
16+
from dataclasses import dataclass
1617
from difflib import Differ
1718
from functools import reduce
1819
from itertools import islice
1920
from pathlib import Path
2021
from typing import Callable
22+
from typing import Dict
23+
from typing import List
24+
from typing import NamedTuple
2125

2226
from rich.console import Console
2327
from rich.logging import RichHandler
2428
from rich.panel import Panel
2529
from rich.progress import track
30+
from rich.rule import Rule
2631

2732
console = Console()
2833
logging.basicConfig(
@@ -66,103 +71,203 @@ def write_file(path: Path, content: str) -> bool:
6671
return False
6772

6873

69-
def preview_changes(original: str, processed: str, context_lines: int = 2) -> None:
70-
"""Show a preview of the changes made."""
71-
console.print("\n[yellow]Preview of changes:[/yellow]")
74+
@dataclass
75+
class DiffStats:
76+
original_lines: int
77+
processed_lines: int
78+
difference: int
7279

73-
# Basic statistics
74-
orig_lines = original.count("\n")
75-
proc_lines = processed.count("\n")
76-
diff_lines = proc_lines - orig_lines
7780

78-
stats_panel = Panel(
79-
f"Original lines: {orig_lines}\n"
80-
f"Processed lines: {proc_lines}\n"
81-
f"Difference: {diff_lines:+d} lines",
82-
title="Statistics",
83-
border_style="blue",
81+
class DiffLine(NamedTuple):
82+
orig_line_no: int
83+
proc_line_no: int
84+
change_type: str
85+
content: str
86+
87+
88+
def calculate_stats(original: str, processed: str) -> DiffStats:
89+
return DiffStats(
90+
original_lines=original.count("\n"),
91+
processed_lines=processed.count("\n"),
92+
difference=processed.count("\n") - original.count("\n"),
8493
)
85-
console.print(stats_panel)
8694

87-
# Create diff
88-
differ = Differ()
89-
diff = list(differ.compare(original.splitlines(), processed.splitlines()))
9095

91-
# Find changed line groups with context
96+
def create_diff_lines(diff_output: List[str]) -> List[DiffLine]:
97+
"""Convert raw diff output into structured DiffLine objects with line numbers."""
98+
diff_lines = []
99+
orig_line_no = proc_line_no = 0
100+
101+
for line in diff_output:
102+
if line.startswith("? "): # Skip hint lines
103+
continue
104+
105+
change_type = line[0:2]
106+
content = line[2:]
107+
108+
current_orig = orig_line_no if change_type in (" ", "- ") else 0
109+
current_proc = proc_line_no if change_type in (" ", "+ ") else 0
110+
111+
diff_lines.append(DiffLine(current_orig, current_proc, change_type, content))
112+
113+
# Update line numbers
114+
if change_type == " ":
115+
orig_line_no += 1
116+
proc_line_no += 1
117+
elif change_type == "- ":
118+
orig_line_no += 1
119+
elif change_type == "+ ":
120+
proc_line_no += 1
121+
122+
return diff_lines
123+
124+
125+
def group_changes(
126+
diff_lines: List[DiffLine], context_lines: int = 5
127+
) -> List[List[DiffLine]]:
128+
"""Group changes with their context lines."""
92129
changes = []
93130
current_group = []
94131
in_change = False
95-
last_change_line = -1
132+
last_change_idx = -1
96133

97-
for i, line in enumerate(diff):
98-
if line.startswith("? "): # Skip hint lines
99-
continue
134+
for i, line in enumerate(diff_lines):
135+
is_change = line.change_type in ("- ", "+ ")
100136

101-
is_change = line.startswith(("- ", "+ "))
102137
if is_change:
103-
if not in_change: # Start of a new change group
104-
start = max(0, i - context_lines)
105-
# If we're close to previous group, connect them
106-
if start <= last_change_line + context_lines:
107-
start = last_change_line + 1
138+
if not in_change:
139+
# Start of a new change group
140+
start_idx = max(0, i - context_lines)
141+
142+
# Connect nearby groups or start new group
143+
if start_idx <= last_change_idx + context_lines:
144+
start_idx = last_change_idx + 1
108145
else:
109146
if current_group:
110147
changes.append(current_group)
111148
current_group = []
112-
# Add previous context
113-
current_group.extend(
114-
l for l in diff[start:i] if not l.startswith("? ")
115-
)
149+
# Add leading context
150+
current_group.extend(diff_lines[start_idx:i])
151+
116152
current_group.append(line)
117153
in_change = True
118-
last_change_line = i
119-
else:
120-
if in_change:
121-
# Add following context
122-
following_context = list(
123-
islice(
124-
(l for l in diff[i:] if not l.startswith("? ")), context_lines
125-
)
154+
last_change_idx = i
155+
156+
elif in_change:
157+
# Add trailing context
158+
following_context = list(
159+
islice(
160+
(l for l in diff_lines[i:] if l.change_type == " "), context_lines
126161
)
127-
if following_context: # Only extend if we have context to add
128-
current_group.extend(following_context)
129-
in_change = False
162+
)
163+
current_group.extend(following_context)
164+
in_change = False
130165

131166
if current_group:
132167
changes.append(current_group)
133168

134-
# Format and display the changes
135-
formatted_output = []
136-
for i, group in enumerate(changes):
137-
if i > 0:
138-
formatted_output.append(
139-
"[bright_black]⋮ skipped unchanged content ⋮[/bright_black]"
140-
)
169+
return changes
141170

142-
# Track the last line to avoid duplicates
143-
last_line = None
144171

145-
for line in group:
146-
# Skip if this line is the same as the last one
147-
if line == last_line:
148-
continue
172+
def get_changes(
173+
original: str, processed: str
174+
) -> tuple[Dict[str, int], List[List[DiffLine]]]:
175+
"""Generate diff information and statistics."""
176+
# Get basic statistics
177+
stats = calculate_stats(original, processed)
178+
179+
# Create and process diff
180+
differ = Differ()
181+
diff_output = list(differ.compare(original.splitlines(), processed.splitlines()))
182+
diff_lines = create_diff_lines(diff_output)
183+
grouped_changes = group_changes(diff_lines)
184+
185+
return vars(stats), grouped_changes
186+
187+
188+
@dataclass
189+
class ChangeGroup:
190+
orig_no: int
191+
proc_no: int
192+
change_type: str
193+
content: str
194+
195+
def format_line_info(self) -> str:
196+
"""Format the line numbers and separator based on change type."""
197+
if self.change_type == " ":
198+
return f"[bright_black]{self.orig_no:4d}{self.proc_no:4d}│[/bright_black]"
199+
elif self.change_type == "- ":
200+
return f"[bright_black]{self.orig_no:4d}│ │[/bright_black]"
201+
else: # "+" case
202+
return f"[bright_black] │{self.proc_no:4d}│[/bright_black]"
203+
204+
def format_content(self) -> str:
205+
"""Format the content based on change type."""
206+
if self.change_type == " ":
207+
return f"[white]{self.content}[/white]"
208+
elif self.change_type == "- ":
209+
return f"[red]- {self.content}[/red]"
210+
else: # "+" case
211+
return f"[green]+ {self.content}[/green]"
212+
213+
214+
def create_stats_panel(stats: dict) -> Panel:
215+
"""Create a formatted statistics panel."""
216+
stats_content = (
217+
f"Original lines: {stats['original_lines']}\n"
218+
f"Processed lines: {stats['processed_lines']}\n"
219+
f"Difference: {stats['difference']:+d} lines"
220+
)
221+
return Panel(
222+
stats_content,
223+
title="Statistics",
224+
border_style="blue",
225+
)
226+
149227

150-
if line.startswith(" "): # unchanged
151-
formatted_output.append(f"[white]{line[2:]}[/white]")
152-
elif line.startswith("- "): # removed
153-
formatted_output.append(f"[red]━ {line[2:]}[/red]")
154-
elif line.startswith("+ "): # added
155-
formatted_output.append(f"[green]+ {line[2:]}[/green]")
228+
def create_separator(prev_group: List[tuple], current_group: List[tuple]) -> Rule:
229+
"""Create a separator between change groups with skip line information."""
230+
if not prev_group:
231+
return None
156232

157-
last_line = line
233+
last_orig = max(l[0] for l in prev_group if l[0] > 0)
234+
next_orig = min(l[0] for l in current_group if l[0] > 0)
235+
skipped_lines = next_orig - last_orig - 1
158236

159-
console.print(
160-
Panel(
161-
"\n".join(formatted_output),
162-
title="Changes with Context",
163-
border_style="yellow",
237+
if skipped_lines > 0:
238+
return Rule(
239+
f" {skipped_lines} lines skipped ",
240+
style="bright_black",
241+
characters="⋮",
164242
)
165-
)
243+
return Rule(style="bright_black", characters="⋮")
244+
245+
246+
def print_change_group(group: List[tuple]) -> None:
247+
"""Print a group of changes with formatting."""
248+
for orig_no, proc_no, change_type, content in group:
249+
change = ChangeGroup(orig_no, proc_no, change_type, content)
250+
line_info = change.format_line_info()
251+
content_formatted = change.format_content()
252+
console.print(f"{line_info} {content_formatted}")
253+
254+
255+
def preview_changes(original: str, processed: str) -> None:
256+
"""Show a preview of the changes made."""
257+
console.print("\n[yellow]Preview of changes:[/yellow]")
258+
259+
# Get diff information and show statistics
260+
stats, changes = get_changes(original, processed)
261+
console.print(create_stats_panel(stats))
262+
263+
# Print changes with separators between groups
264+
for i, group in enumerate(changes):
265+
if i > 0:
266+
separator = create_separator(changes[i - 1], group)
267+
if separator:
268+
console.print(separator)
269+
270+
print_change_group(group)
166271

167272

168273
def process_readme(

0 commit comments

Comments
 (0)