diff --git a/gtm b/gtm index 18f0d84..550f7f1 100755 --- a/gtm +++ b/gtm @@ -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))