refactor: Implement smart line matching for scroll position preservation

This commit is contained in:
n loewen (aider) 2025-06-07 20:26:09 +01:00
parent bc1c07ba29
commit a985eacd0e
1 changed files with 62 additions and 5 deletions

67
gtm
View File

@ -18,6 +18,44 @@ def get_file_at_commit(commit_hash, filename):
result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout.splitlines()
def find_best_matching_line(reference_line, file_lines, max_lines=None):
"""Find the best matching line in file_lines that matches reference_line.
Returns the line index or None if no good match is found."""
if not reference_line or not file_lines:
return None
# First try exact match
for i, line in enumerate(file_lines):
if line == reference_line:
return i
# If no exact match, try to find the most similar line
# Only search through a reasonable number of lines for performance
search_lines = file_lines[:max_lines] if max_lines else file_lines
best_match = None
best_score = 0
for i, line in enumerate(search_lines):
# Simple similarity score: count of common characters
score = sum(1 for a, b in zip(reference_line, line) if a == b)
# Adjust score based on length difference
length_diff = abs(len(reference_line) - len(line))
adjusted_score = score - (length_diff * 0.5)
if adjusted_score > best_score:
best_score = adjusted_score
best_match = i
# Only return a match if it's reasonably good
# (at least 60% of the shorter string length)
min_length = min(len(reference_line), 1) # Avoid division by zero
if best_score > (min_length * 0.6):
return best_match
return None
def get_diff_info(current_commit, prev_commit, filename):
"""Get diff information between two commits for a file"""
if not prev_commit:
@ -129,10 +167,15 @@ def main(stdscr, filename, show_whole_diff=False, show_additions=False, show_del
# Calculate current scroll position as percentage before changing commits
scroll_percentage = 0
reference_line = None
if len(file_lines) > 0:
max_scroll = max(0, len(file_lines) - (height - 1))
if max_scroll > 0:
scroll_percentage = scroll_offset / max_scroll
# Store the content of the top visible line as reference
if scroll_offset < len(file_lines):
reference_line = file_lines[scroll_offset]
# Only fetch file content when commit changes
if (key in [curses.KEY_DOWN, curses.KEY_UP, ord('j'), ord('k'),
@ -148,12 +191,26 @@ def main(stdscr, filename, show_whole_diff=False, show_additions=False, show_del
if show_whole_diff or show_additions or show_deletions:
added_lines, deleted_lines = get_diff_info(commit_hash, prev_commit_hash, filename)
# Apply the saved scroll percentage to the new file
max_scroll = max(0, len(file_lines) - (height - 1))
if max_scroll > 0:
scroll_offset = int(scroll_percentage * max_scroll)
# Try to find the same line in the new file version
if reference_line:
# Limit search to first 1000 lines for performance
matching_line_idx = find_best_matching_line(reference_line, file_lines, 1000)
if matching_line_idx is not None:
scroll_offset = matching_line_idx
else:
# Fall back to percentage-based scrolling
max_scroll = max(0, len(file_lines) - (height - 1))
if max_scroll > 0:
scroll_offset = int(scroll_percentage * max_scroll)
else:
scroll_offset = 0
else:
scroll_offset = 0
# Fall back to percentage-based scrolling
max_scroll = max(0, len(file_lines) - (height - 1))
if max_scroll > 0:
scroll_offset = int(scroll_percentage * max_scroll)
else:
scroll_offset = 0
# Recalculate max_scroll and ensure scroll_offset is within bounds
max_scroll = max(0, len(file_lines) - (height - 1))