feat: Add line numbers with color-coded display and toggle option
This commit is contained in:
parent
cfa96a436f
commit
e4b65b98d1
47
gtm
47
gtm
|
|
@ -133,6 +133,9 @@ class AppState:
|
||||||
# Line wrapping settings
|
# Line wrapping settings
|
||||||
wrap_lines: bool = True
|
wrap_lines: bool = True
|
||||||
|
|
||||||
|
# Line numbers settings
|
||||||
|
show_line_numbers: bool = False
|
||||||
|
|
||||||
# Change navigation
|
# Change navigation
|
||||||
change_blocks: List[Tuple[int, int]] = field(default_factory=list) # (start_line, end_line) of each change block
|
change_blocks: List[Tuple[int, int]] = field(default_factory=list) # (start_line, end_line) of each change block
|
||||||
current_change_idx: int = -1 # -1 means no change is selected
|
current_change_idx: int = -1 # -1 means no change is selected
|
||||||
|
|
@ -356,6 +359,18 @@ def jump_to_prev_change(state: AppState) -> AppState:
|
||||||
def draw_right_pane(stdscr, state):
|
def draw_right_pane(stdscr, state):
|
||||||
# If sidebar is hidden, right pane starts at column 0
|
# If sidebar is hidden, right pane starts at column 0
|
||||||
right_start = 0 if not state.show_sidebar else state.divider_col + 2
|
right_start = 0 if not state.show_sidebar else state.divider_col + 2
|
||||||
|
|
||||||
|
# Calculate line number width if enabled
|
||||||
|
line_num_width = 0
|
||||||
|
if state.show_line_numbers:
|
||||||
|
# Width based on the number of digits in the largest line number
|
||||||
|
max_line_num = len(state.file_lines)
|
||||||
|
line_num_width = len(str(max_line_num)) + 1 # +1 for spacing
|
||||||
|
|
||||||
|
# Adjust right pane start position and width for line numbers
|
||||||
|
if state.show_line_numbers:
|
||||||
|
right_start += line_num_width
|
||||||
|
|
||||||
right_width = state.width - right_start - 1
|
right_width = state.width - right_start - 1
|
||||||
|
|
||||||
max_scroll = max(0, len(state.file_lines) - (state.height - 1))
|
max_scroll = max(0, len(state.file_lines) - (state.height - 1))
|
||||||
|
|
@ -375,7 +390,8 @@ def draw_right_pane(stdscr, state):
|
||||||
line_num = i + state.right_scroll_offset + 1
|
line_num = i + state.right_scroll_offset + 1
|
||||||
if (state.show_whole_diff or state.show_deletions) and line_num in deleted_line_map:
|
if (state.show_whole_diff or state.show_deletions) and line_num in deleted_line_map:
|
||||||
for del_content in deleted_line_map[line_num]:
|
for del_content in deleted_line_map[line_num]:
|
||||||
display_lines.append({'type': 'deleted', 'content': del_content})
|
# For deleted lines, use the same line number
|
||||||
|
display_lines.append({'type': 'deleted', 'content': del_content, 'line_num': line_num})
|
||||||
|
|
||||||
is_added = False
|
is_added = False
|
||||||
if state.show_whole_diff or state.show_additions:
|
if state.show_whole_diff or state.show_additions:
|
||||||
|
|
@ -384,7 +400,7 @@ def draw_right_pane(stdscr, state):
|
||||||
is_added = True
|
is_added = True
|
||||||
break
|
break
|
||||||
|
|
||||||
display_lines.append({'type': 'added' if is_added else 'regular', 'content': line})
|
display_lines.append({'type': 'added' if is_added else 'regular', 'content': line, 'line_num': line_num})
|
||||||
|
|
||||||
display_row = 0
|
display_row = 0
|
||||||
for line_info in display_lines:
|
for line_info in display_lines:
|
||||||
|
|
@ -393,6 +409,20 @@ def draw_right_pane(stdscr, state):
|
||||||
|
|
||||||
line_type = line_info['type']
|
line_type = line_info['type']
|
||||||
content = line_info['content']
|
content = line_info['content']
|
||||||
|
line_num = line_info.get('line_num', 0)
|
||||||
|
|
||||||
|
# Draw line number if enabled
|
||||||
|
if state.show_line_numbers:
|
||||||
|
line_num_str = str(line_num).rjust(line_num_width - 1) + " "
|
||||||
|
line_num_pos = (0 if not state.show_sidebar else state.divider_col + 2)
|
||||||
|
|
||||||
|
# Use different color for line numbers
|
||||||
|
if line_type == 'added':
|
||||||
|
stdscr.addstr(display_row, line_num_pos, line_num_str, curses.color_pair(5))
|
||||||
|
elif line_type == 'deleted':
|
||||||
|
stdscr.addstr(display_row, line_num_pos, line_num_str, curses.color_pair(6))
|
||||||
|
else:
|
||||||
|
stdscr.addstr(display_row, line_num_pos, line_num_str, curses.color_pair(7))
|
||||||
|
|
||||||
# Determine prefix based on line type and diff mode
|
# Determine prefix based on line type and diff mode
|
||||||
prefix = ""
|
prefix = ""
|
||||||
|
|
@ -528,13 +558,14 @@ def draw_status_bars(stdscr, state):
|
||||||
if len(parts) >= 4:
|
if len(parts) >= 4:
|
||||||
commit_message = parts[3]
|
commit_message = parts[3]
|
||||||
|
|
||||||
# Add wrap indicator and change position to commit message
|
# Add indicators to commit message
|
||||||
wrap_indicator = "" if state.wrap_lines else "[NW] "
|
wrap_indicator = "" if state.wrap_lines else "[NW] "
|
||||||
|
line_num_indicator = "[LN] " if state.show_line_numbers else ""
|
||||||
change_indicator = ""
|
change_indicator = ""
|
||||||
if state.change_blocks and state.current_change_idx != -1:
|
if state.change_blocks and state.current_change_idx != -1:
|
||||||
change_indicator = f"[Change {state.current_change_idx + 1}/{len(state.change_blocks)}] "
|
change_indicator = f"[Change {state.current_change_idx + 1}/{len(state.change_blocks)}] "
|
||||||
|
|
||||||
commit_message = wrap_indicator + change_indicator + commit_message
|
commit_message = wrap_indicator + line_num_indicator + change_indicator + commit_message
|
||||||
|
|
||||||
# Status bar percentages
|
# Status bar percentages
|
||||||
if len(state.file_lines) > 0:
|
if len(state.file_lines) > 0:
|
||||||
|
|
@ -814,6 +845,8 @@ def handle_keyboard_input(key, state: AppState) -> AppState:
|
||||||
return toggle_sidebar(state)
|
return toggle_sidebar(state)
|
||||||
elif key == ord('w'):
|
elif key == ord('w'):
|
||||||
return replace(state, wrap_lines=not state.wrap_lines)
|
return replace(state, wrap_lines=not state.wrap_lines)
|
||||||
|
elif key == ord('L'): # Capital L to toggle line numbers
|
||||||
|
return replace(state, show_line_numbers=not state.show_line_numbers)
|
||||||
elif key in [110, ord('n')]: # ASCII code for 'n'
|
elif key in [110, ord('n')]: # ASCII code for 'n'
|
||||||
if state.show_whole_diff or state.show_additions or state.show_deletions:
|
if state.show_whole_diff or state.show_additions or state.show_deletions:
|
||||||
return jump_to_next_change(state)
|
return jump_to_next_change(state)
|
||||||
|
|
@ -864,6 +897,9 @@ def main(stdscr, filename, show_diff, show_add, show_del, mouse, wrap_lines=True
|
||||||
curses.init_pair(2, curses.COLOR_WHITE, 0) # Inactive pane: white on black
|
curses.init_pair(2, curses.COLOR_WHITE, 0) # Inactive pane: white on black
|
||||||
curses.init_pair(3, curses.COLOR_GREEN, -1) # Added lines
|
curses.init_pair(3, curses.COLOR_GREEN, -1) # Added lines
|
||||||
curses.init_pair(4, curses.COLOR_RED, -1) # Deleted lines
|
curses.init_pair(4, curses.COLOR_RED, -1) # Deleted lines
|
||||||
|
curses.init_pair(5, curses.COLOR_GREEN, -1) # Line numbers for added lines
|
||||||
|
curses.init_pair(6, curses.COLOR_RED, -1) # Line numbers for deleted lines
|
||||||
|
curses.init_pair(7, curses.COLOR_BLUE, -1) # Regular line numbers
|
||||||
|
|
||||||
height, width = stdscr.getmaxyx()
|
height, width = stdscr.getmaxyx()
|
||||||
state = AppState(
|
state = AppState(
|
||||||
|
|
@ -930,6 +966,7 @@ if __name__ == "__main__":
|
||||||
parser.add_argument("--diff-deletions", action="store_true", help="Show deleted lines in red")
|
parser.add_argument("--diff-deletions", action="store_true", help="Show deleted lines in red")
|
||||||
parser.add_argument("--no-mouse", action="store_true", help="Disable mouse support")
|
parser.add_argument("--no-mouse", action="store_true", help="Disable mouse support")
|
||||||
parser.add_argument("--no-wrap", action="store_true", help="Disable line wrapping")
|
parser.add_argument("--no-wrap", action="store_true", help="Disable line wrapping")
|
||||||
|
parser.add_argument("--line-numbers", action="store_true", help="Show line numbers")
|
||||||
parser.add_argument("-v", "--version", action="store_true", help="Show version number")
|
parser.add_argument("-v", "--version", action="store_true", help="Show version number")
|
||||||
parser.add_argument("filename", nargs="?", help="File to view history for")
|
parser.add_argument("filename", nargs="?", help="File to view history for")
|
||||||
|
|
||||||
|
|
@ -956,4 +993,4 @@ if __name__ == "__main__":
|
||||||
print(f" git commit -m 'Add {os.path.basename(filename)}'")
|
print(f" git commit -m 'Add {os.path.basename(filename)}'")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
curses.wrapper(main, filename, args.diff, args.diff_additions, args.diff_deletions, not args.no_mouse, not args.no_wrap)
|
curses.wrapper(main, filename, args.diff, args.diff_additions, args.diff_deletions, not args.no_mouse, not args.no_wrap, args.line_numbers)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue