refactor: Implement smart line matching for scroll position preservation
This commit is contained in:
parent
bc1c07ba29
commit
a985eacd0e
67
gtm
67
gtm
|
|
@ -18,6 +18,44 @@ def get_file_at_commit(commit_hash, filename):
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
return result.stdout.splitlines()
|
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):
|
def get_diff_info(current_commit, prev_commit, filename):
|
||||||
"""Get diff information between two commits for a file"""
|
"""Get diff information between two commits for a file"""
|
||||||
if not prev_commit:
|
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
|
# Calculate current scroll position as percentage before changing commits
|
||||||
scroll_percentage = 0
|
scroll_percentage = 0
|
||||||
|
reference_line = None
|
||||||
if len(file_lines) > 0:
|
if len(file_lines) > 0:
|
||||||
max_scroll = max(0, len(file_lines) - (height - 1))
|
max_scroll = max(0, len(file_lines) - (height - 1))
|
||||||
if max_scroll > 0:
|
if max_scroll > 0:
|
||||||
scroll_percentage = scroll_offset / max_scroll
|
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
|
# Only fetch file content when commit changes
|
||||||
if (key in [curses.KEY_DOWN, curses.KEY_UP, ord('j'), ord('k'),
|
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:
|
if show_whole_diff or show_additions or show_deletions:
|
||||||
added_lines, deleted_lines = get_diff_info(commit_hash, prev_commit_hash, filename)
|
added_lines, deleted_lines = get_diff_info(commit_hash, prev_commit_hash, filename)
|
||||||
|
|
||||||
# Apply the saved scroll percentage to the new file
|
# Try to find the same line in the new file version
|
||||||
max_scroll = max(0, len(file_lines) - (height - 1))
|
if reference_line:
|
||||||
if max_scroll > 0:
|
# Limit search to first 1000 lines for performance
|
||||||
scroll_offset = int(scroll_percentage * max_scroll)
|
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:
|
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
|
# Recalculate max_scroll and ensure scroll_offset is within bounds
|
||||||
max_scroll = max(0, len(file_lines) - (height - 1))
|
max_scroll = max(0, len(file_lines) - (height - 1))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue