From da321d1271c482eef32dab545da9bdd17cf08b08 Mon Sep 17 00:00:00 2001 From: marcusDenslow Date: Sun, 15 Jun 2025 16:06:28 +0200 Subject: [PATCH] fixed border rendering issues after i added the * indicator to commits and stashes, the left borded dissapeared, i forgot to indent the line with one to make space for the indicators. fixed text in the view pane to stop / wrap before the last line, meaning that the last character is not in the border anymore --- ncurses_diff_viewer.c | 1276 +++++++++++++++++++++++------------------ ncurses_diff_viewer.o | Bin 221200 -> 221128 bytes shell | Bin 558112 -> 557840 bytes 3 files changed, 708 insertions(+), 568 deletions(-) diff --git a/ncurses_diff_viewer.c b/ncurses_diff_viewer.c index 4d8f3fe..746347b 100644 --- a/ncurses_diff_viewer.c +++ b/ncurses_diff_viewer.c @@ -104,13 +104,13 @@ int init_ncurses_diff_viewer(NCursesDiffViewer *viewer) { return 0; memset(viewer, 0, sizeof(NCursesDiffViewer)); - + // Initialize fuzzy search init_fuzzy_search(viewer); - + // Initialize grep search init_grep_search(viewer); - + viewer->selected_file = 0; viewer->file_scroll_offset = 0; viewer->file_cursor_line = 0; @@ -2423,7 +2423,7 @@ void render_commit_list_window(NCursesDiffViewer *viewer) { // Check if this commit line should be highlighted int is_selected_commit = (i == viewer->selected_commit && viewer->current_mode == NCURSES_MODE_COMMIT_LIST); - + // Check if this commit is currently being viewed int is_being_viewed = (i == viewer->selected_commit && viewer->current_mode == NCURSES_MODE_COMMIT_VIEW); @@ -2436,17 +2436,17 @@ void render_commit_list_window(NCursesDiffViewer *viewer) { // Show view indicator (currently being viewed) if (is_being_viewed) { wattron(viewer->commit_list_win, COLOR_PAIR(1)); // Green for active view - mvwprintw(viewer->commit_list_win, y, 0, "*"); + mvwprintw(viewer->commit_list_win, y, 1, "*"); wattroff(viewer->commit_list_win, COLOR_PAIR(1)); } else { - mvwprintw(viewer->commit_list_win, y, 0, " "); + mvwprintw(viewer->commit_list_win, y, 1, " "); } // Show selection indicator if (is_selected_commit) { - mvwprintw(viewer->commit_list_win, y, 1, ">"); + mvwprintw(viewer->commit_list_win, y, 2, ">"); } else { - mvwprintw(viewer->commit_list_win, y, 1, " "); + mvwprintw(viewer->commit_list_win, y, 2, " "); } // Show commit hash with color based on push status @@ -2525,60 +2525,61 @@ void render_file_content_window(NCursesDiffViewer *viewer) { viewer->current_mode == NCURSES_MODE_BRANCH_VIEW || viewer->current_mode == NCURSES_MODE_STASH_LIST || viewer->current_mode == NCURSES_MODE_STASH_VIEW) { - + // Add title based on current mode - const char* title = ""; + const char *title = ""; switch (viewer->current_mode) { - case NCURSES_MODE_FILE_LIST: - title = " File Diff Preview "; - break; - case NCURSES_MODE_COMMIT_LIST: - title = " Commit Details "; - break; - case NCURSES_MODE_COMMIT_VIEW: - title = " Commit Diff "; - break; - case NCURSES_MODE_BRANCH_LIST: - title = " Branch Commits "; - break; - case NCURSES_MODE_BRANCH_VIEW: - title = " Branch Details "; - break; - case NCURSES_MODE_STASH_LIST: - title = " Stash Details "; - break; - case NCURSES_MODE_STASH_VIEW: - title = " Stash Diff "; - break; - default: - title = " Preview "; - break; + case NCURSES_MODE_FILE_LIST: + title = " File Diff Preview "; + break; + case NCURSES_MODE_COMMIT_LIST: + title = " Commit Details "; + break; + case NCURSES_MODE_COMMIT_VIEW: + title = " Commit Diff "; + break; + case NCURSES_MODE_BRANCH_LIST: + title = " Branch Commits "; + break; + case NCURSES_MODE_BRANCH_VIEW: + title = " Branch Details "; + break; + case NCURSES_MODE_STASH_LIST: + title = " Stash Details "; + break; + case NCURSES_MODE_STASH_VIEW: + title = " Stash Diff "; + break; + default: + title = " Preview "; + break; } mvwprintw(viewer->file_content_win, 0, 2, "%s", title); - + // Render preview content using the loaded file_lines if (viewer->file_line_count > 0) { int max_lines_visible = height - 2; int display_count = 0; - + for (int i = viewer->file_scroll_offset; i < viewer->file_line_count && display_count < max_lines_visible; i++) { - + NCursesFileLine *line = &viewer->file_lines[i]; int is_cursor_line = (i == viewer->file_cursor_line); - + // Calculate how many display lines this logical line will need - int line_height = calculate_wrapped_line_height(line->line, width - 4); - + int line_height = + calculate_wrapped_line_height(line->line, width - 4); + // Skip if this line would exceed remaining space if (display_count + line_height > max_lines_visible) { break; } - + int y = display_count + 1; int color_pair = 0; - + // Determine color based on line type if (line->type == '@') { color_pair = 3; // Cyan for hunk headers @@ -2587,18 +2588,20 @@ void render_file_content_window(NCursesDiffViewer *viewer) { } else if (line->type == '-') { color_pair = 2; // Red for deletions } - + // Render the line with wrapping - int rows_used = render_wrapped_line(viewer->file_content_win, line->line, - y, 1, width, line_height, color_pair, is_cursor_line); + int rows_used = render_wrapped_line( + viewer->file_content_win, line->line, y, 1, width - 2, line_height, + color_pair, is_cursor_line); display_count += rows_used; } } else { // No content to show - mvwprintw(viewer->file_content_win, height/2, (width - 15)/2, "No preview available"); + mvwprintw(viewer->file_content_win, height / 2, (width - 15) / 2, + "No preview available"); } } - + wrefresh(viewer->file_content_win); return; } @@ -2630,11 +2633,12 @@ void render_file_content_window(NCursesDiffViewer *viewer) { i++) { NCursesFileLine *line = &viewer->file_lines[i]; - int is_cursor_line = (i == viewer->file_cursor_line && viewer->active_pane == 0); - + int is_cursor_line = + (i == viewer->file_cursor_line && viewer->active_pane == 0); + // Calculate how many display lines this logical line will need int line_height = calculate_wrapped_line_height(line->line, width - 4); - + // Skip if this line would exceed remaining space if (unstaged_display_count + line_height > unstaged_height - 1) { break; @@ -2642,7 +2646,7 @@ void render_file_content_window(NCursesDiffViewer *viewer) { int y = unstaged_display_count + 1; int color_pair = 0; - + // Determine color based on line type if (line->type == '@') { color_pair = 3; // Cyan for hunk headers @@ -2666,17 +2670,20 @@ void render_file_content_window(NCursesDiffViewer *viewer) { if (is_cursor_line) { wattroff(viewer->file_content_win, A_REVERSE); } - - // Then render the line content starting from column 2, skipping first char + + // Then render the line content starting from column 2, skipping first + // char char line_without_prefix[1024]; strcpy(line_without_prefix, line->line + 1); - int rows_used = render_wrapped_line(viewer->file_content_win, line_without_prefix, - y, 2, width, line_height, color_pair, is_cursor_line); + int rows_used = render_wrapped_line( + viewer->file_content_win, line_without_prefix, y, 2, width - 2, + line_height, color_pair, is_cursor_line); unstaged_display_count += rows_used; } else { // Regular line rendering - int rows_used = render_wrapped_line(viewer->file_content_win, line->line, - y, 1, width, line_height, color_pair, is_cursor_line); + int rows_used = + render_wrapped_line(viewer->file_content_win, line->line, y, 1, width - 2, + line_height, color_pair, is_cursor_line); unstaged_display_count += rows_used; } } @@ -2698,11 +2705,12 @@ void render_file_content_window(NCursesDiffViewer *viewer) { i++) { NCursesFileLine *line = &viewer->staged_lines[i]; - int is_cursor_line = (i == viewer->staged_cursor_line && viewer->active_pane == 1); - + int is_cursor_line = + (i == viewer->staged_cursor_line && viewer->active_pane == 1); + // Calculate how many display lines this logical line will need int line_height = calculate_wrapped_line_height(line->line, width - 4); - + // Skip if this line would exceed remaining space if (staged_display_count + line_height > staged_height - 1) { break; @@ -2710,7 +2718,7 @@ void render_file_content_window(NCursesDiffViewer *viewer) { int y = split_line + 1 + staged_display_count; int color_pair = 0; - + // Determine color based on line type if (line->type == '+') { color_pair = 1; // Green for additions @@ -2721,8 +2729,9 @@ void render_file_content_window(NCursesDiffViewer *viewer) { } // Render the line with wrapping - int rows_used = render_wrapped_line(viewer->file_content_win, line->line, - y, 1, width, line_height, color_pair, is_cursor_line); + int rows_used = + render_wrapped_line(viewer->file_content_win, line->line, y, 1, + width - 2, line_height, color_pair, is_cursor_line); staged_display_count += rows_used; } @@ -3238,7 +3247,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { if (viewer->fuzzy_search_active) { return handle_fuzzy_search_input(viewer, key); } - + // Handle grep search input if active if (viewer->grep_search_active) { return handle_grep_search_input(viewer, key); @@ -3756,7 +3765,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { render_status_bar(viewer); } break; - + case '/': // Forward slash - enter grep search mode enter_grep_search_mode(viewer); break; @@ -3887,7 +3896,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { viewer->critical_operation_in_progress = 0; } break; - + case '/': // Forward slash - enter grep search mode enter_grep_search_mode(viewer); break; @@ -4214,7 +4223,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { viewer->critical_operation_in_progress = 0; } break; - + case '/': // Forward slash - enter grep search mode enter_grep_search_mode(viewer); break; @@ -4225,7 +4234,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { case 27: // ESC viewer->current_mode = NCURSES_MODE_COMMIT_LIST; // Return to commit list mode - viewer->split_view_mode = 0; // Exit split view mode + viewer->split_view_mode = 0; // Exit split view mode break; case KEY_UP: @@ -4383,7 +4392,7 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { case 27: // ESC viewer->current_mode = NCURSES_MODE_BRANCH_LIST; // Return to branch list mode - viewer->split_view_mode = 0; // Exit split view mode + viewer->split_view_mode = 0; // Exit split view mode break; case KEY_UP: @@ -4563,7 +4572,8 @@ int run_ncurses_diff_viewer(void) { // Update preview based on current selection update_preview_for_current_selection(&viewer); - // Skip main window rendering if fuzzy or grep search is active to prevent flickering + // Skip main window rendering if fuzzy or grep search is active to prevent + // flickering if (!viewer.fuzzy_search_active && !viewer.grep_search_active) { render_file_list_window(&viewer); render_file_content_window(&viewer); @@ -4572,7 +4582,7 @@ int run_ncurses_diff_viewer(void) { render_stash_list_window(&viewer); render_status_bar(&viewer); } - + // Always render search overlays (they handle their own visibility) render_fuzzy_search(&viewer); render_grep_search(&viewer); @@ -5541,7 +5551,7 @@ void render_stash_list_window(NCursesDiffViewer *viewer) { // Check if this stash line should be highlighted int is_selected_stash = (i == viewer->selected_stash && viewer->current_mode == NCURSES_MODE_STASH_LIST); - + // Check if this stash is currently being viewed int is_being_viewed = (i == viewer->selected_stash && viewer->current_mode == NCURSES_MODE_STASH_VIEW); @@ -5554,17 +5564,17 @@ void render_stash_list_window(NCursesDiffViewer *viewer) { // Show view indicator (currently being viewed) if (is_being_viewed) { wattron(viewer->stash_list_win, COLOR_PAIR(1)); // Green for active view - mvwprintw(viewer->stash_list_win, y, 0, "*"); + mvwprintw(viewer->stash_list_win, y, 1, "*"); wattroff(viewer->stash_list_win, COLOR_PAIR(1)); } else { - mvwprintw(viewer->stash_list_win, y, 0, " "); + mvwprintw(viewer->stash_list_win, y, 1, " "); } // Show selection indicator if (is_selected_stash) { - mvwprintw(viewer->stash_list_win, y, 1, ">"); + mvwprintw(viewer->stash_list_win, y, 2, ">"); } else { - mvwprintw(viewer->stash_list_win, y, 1, " "); + mvwprintw(viewer->stash_list_win, y, 2, " "); } // Show stash info (truncated to fit panel) @@ -6205,52 +6215,65 @@ void check_background_fetch(NCursesDiffViewer *viewer) { * Update preview based on current mode and selection */ void update_preview_for_current_selection(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + static NCursesViewMode last_mode = (NCursesViewMode)-1; static int last_file = -1; static int last_commit = -1; static int last_branch = -1; static int last_stash = -1; - + int needs_update = 0; - + if (viewer->current_mode == NCURSES_MODE_FILE_LIST) { - if (last_mode != viewer->current_mode || last_file != viewer->selected_file) { - if (viewer->file_count > 0 && viewer->selected_file < viewer->file_count) { + if (last_mode != viewer->current_mode || + last_file != viewer->selected_file) { + if (viewer->file_count > 0 && + viewer->selected_file < viewer->file_count) { // Load diff preview instead of raw file content - load_file_with_staging_info(viewer, viewer->files[viewer->selected_file].filename); + load_file_with_staging_info( + viewer, viewer->files[viewer->selected_file].filename); } last_file = viewer->selected_file; needs_update = 1; } } else if (viewer->current_mode == NCURSES_MODE_COMMIT_LIST) { - if (last_mode != viewer->current_mode || last_commit != viewer->selected_commit) { - if (viewer->commit_count > 0 && viewer->selected_commit < viewer->commit_count) { - load_commit_for_viewing(viewer, viewer->commits[viewer->selected_commit].hash); + if (last_mode != viewer->current_mode || + last_commit != viewer->selected_commit) { + if (viewer->commit_count > 0 && + viewer->selected_commit < viewer->commit_count) { + load_commit_for_viewing(viewer, + viewer->commits[viewer->selected_commit].hash); } last_commit = viewer->selected_commit; needs_update = 1; } } else if (viewer->current_mode == NCURSES_MODE_BRANCH_LIST) { - if (last_mode != viewer->current_mode || last_branch != viewer->selected_branch) { - if (viewer->branch_count > 0 && viewer->selected_branch < viewer->branch_count) { - load_branch_commits(viewer, viewer->branches[viewer->selected_branch].name); - parse_branch_commits_to_lines(viewer); // Convert to file_lines for preview display + if (last_mode != viewer->current_mode || + last_branch != viewer->selected_branch) { + if (viewer->branch_count > 0 && + viewer->selected_branch < viewer->branch_count) { + load_branch_commits(viewer, + viewer->branches[viewer->selected_branch].name); + parse_branch_commits_to_lines( + viewer); // Convert to file_lines for preview display } last_branch = viewer->selected_branch; needs_update = 1; } } else if (viewer->current_mode == NCURSES_MODE_STASH_LIST) { - if (last_mode != viewer->current_mode || last_stash != viewer->selected_stash) { - if (viewer->stash_count > 0 && viewer->selected_stash < viewer->stash_count) { + if (last_mode != viewer->current_mode || + last_stash != viewer->selected_stash) { + if (viewer->stash_count > 0 && + viewer->selected_stash < viewer->stash_count) { load_stash_for_viewing(viewer, viewer->selected_stash); } last_stash = viewer->selected_stash; needs_update = 1; } } - + if (needs_update) { last_mode = viewer->current_mode; } @@ -6262,19 +6285,19 @@ void update_preview_for_current_selection(NCursesDiffViewer *viewer) { int load_file_preview(NCursesDiffViewer *viewer, const char *filename) { if (!viewer || !filename) return 0; - + viewer->file_line_count = 0; viewer->file_scroll_offset = 0; viewer->file_cursor_line = 0; - + FILE *fp = fopen(filename, "r"); if (!fp) { return 0; } - + char line[1024]; int line_count = 0; - + // Load first 50 lines of the file for preview while (fgets(line, sizeof(line), fp) && line_count < 50 && line_count < MAX_FULL_FILE_LINES) { @@ -6283,11 +6306,12 @@ int load_file_preview(NCursesDiffViewer *viewer, const char *filename) { if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; } - + // Store the line as a context line (no diff markings) - strncpy(viewer->file_lines[line_count].line, line, + strncpy(viewer->file_lines[line_count].line, line, sizeof(viewer->file_lines[line_count].line) - 1); - viewer->file_lines[line_count].line[sizeof(viewer->file_lines[line_count].line) - 1] = '\0'; + viewer->file_lines[line_count] + .line[sizeof(viewer->file_lines[line_count].line) - 1] = '\0'; viewer->file_lines[line_count].type = ' '; // Context line viewer->file_lines[line_count].is_diff_line = 0; viewer->file_lines[line_count].is_staged = 0; @@ -6295,18 +6319,18 @@ int load_file_preview(NCursesDiffViewer *viewer, const char *filename) { viewer->file_lines[line_count].line_number_old = line_count + 1; viewer->file_lines[line_count].line_number_new = line_count + 1; viewer->file_lines[line_count].is_context = 1; - + line_count++; } - + fclose(fp); viewer->file_line_count = line_count; - + // Reset staged content since this is just a preview viewer->staged_line_count = 0; viewer->staged_cursor_line = 0; viewer->staged_scroll_offset = 0; - + return 1; } @@ -6314,33 +6338,34 @@ int load_file_preview(NCursesDiffViewer *viewer, const char *filename) { * Helper function to wrap a long line into multiple display lines * Returns the number of wrapped lines created */ -int wrap_line_to_width(const char *input_line, char wrapped_lines[][1024], int max_lines, int width) { +int wrap_line_to_width(const char *input_line, char wrapped_lines[][1024], + int max_lines, int width) { int input_len = strlen(input_line); int line_count = 0; int input_pos = 0; - + // If line fits in width, just copy it if (input_len <= width) { strcpy(wrapped_lines[0], input_line); return 1; } - + while (input_pos < input_len && line_count < max_lines - 1) { int chars_to_copy = width; - + // Don't exceed remaining input length if (input_pos + chars_to_copy > input_len) { chars_to_copy = input_len - input_pos; } - + // Copy the segment strncpy(wrapped_lines[line_count], input_line + input_pos, chars_to_copy); wrapped_lines[line_count][chars_to_copy] = '\0'; - + input_pos += chars_to_copy; line_count++; } - + return line_count; } @@ -6348,11 +6373,12 @@ int wrap_line_to_width(const char *input_line, char wrapped_lines[][1024], int m * Render a single line with proper wrapping and coloring * Returns the number of display rows used */ -int render_wrapped_line(WINDOW *win, const char *line, int start_y, int start_x, - int width, int max_rows, int color_pair, int reverse) { - char wrapped_lines[10][1024]; // Support up to 10 wrapped lines per original line +int render_wrapped_line(WINDOW *win, const char *line, int start_y, int start_x, + int width, int max_rows, int color_pair, int reverse) { + char wrapped_lines[10] + [1024]; // Support up to 10 wrapped lines per original line int wrap_count = wrap_line_to_width(line, wrapped_lines, 10, width - start_x); - + int rows_used = 0; for (int i = 0; i < wrap_count && rows_used < max_rows; i++) { if (reverse) { @@ -6361,19 +6387,19 @@ int render_wrapped_line(WINDOW *win, const char *line, int start_y, int start_x, if (color_pair > 0) { wattron(win, COLOR_PAIR(color_pair)); } - + mvwprintw(win, start_y + rows_used, start_x, "%s", wrapped_lines[i]); - + if (color_pair > 0) { wattroff(win, COLOR_PAIR(color_pair)); } if (reverse) { wattroff(win, A_REVERSE); } - + rows_used++; } - + return rows_used; } @@ -6517,14 +6543,16 @@ void move_cursor_smart_unstaged(NCursesDiffViewer *viewer, int direction) { getmaxyx(viewer->file_content_win, height, width); int split_line = height / 2; int unstaged_height = split_line - 1; - + // Calculate display positions accounting for wrapped lines int cursor_display_rows = 0; int scroll_display_rows = 0; - + // Count display rows from scroll offset to cursor - for (int i = viewer->file_scroll_offset; i <= viewer->file_cursor_line && i < viewer->file_line_count; i++) { - int line_height = calculate_wrapped_line_height(viewer->file_lines[i].line, width - 4); + for (int i = viewer->file_scroll_offset; + i <= viewer->file_cursor_line && i < viewer->file_line_count; i++) { + int line_height = + calculate_wrapped_line_height(viewer->file_lines[i].line, width - 4); if (i < viewer->file_cursor_line) { cursor_display_rows += line_height; } @@ -6532,20 +6560,22 @@ void move_cursor_smart_unstaged(NCursesDiffViewer *viewer, int direction) { scroll_display_rows += line_height; } } - + if (direction == -1) { // Moving up - ensure cursor stays visible at top if (cursor_display_rows < 2) { // Need to scroll up to make cursor visible int target_rows = 2; int new_scroll_offset = viewer->file_cursor_line; - int accumulated_rows = calculate_wrapped_line_height(viewer->file_lines[viewer->file_cursor_line].line, width - 4); - + int accumulated_rows = calculate_wrapped_line_height( + viewer->file_lines[viewer->file_cursor_line].line, width - 4); + while (new_scroll_offset > 0 && accumulated_rows < target_rows) { new_scroll_offset--; - accumulated_rows += calculate_wrapped_line_height(viewer->file_lines[new_scroll_offset].line, width - 4); + accumulated_rows += calculate_wrapped_line_height( + viewer->file_lines[new_scroll_offset].line, width - 4); } - + viewer->file_scroll_offset = new_scroll_offset; if (viewer->file_scroll_offset < 0) { viewer->file_scroll_offset = 0; @@ -6557,17 +6587,20 @@ void move_cursor_smart_unstaged(NCursesDiffViewer *viewer, int direction) { // Need to scroll down to make cursor visible int target_remaining_rows = unstaged_height - 3; int new_scroll_offset = viewer->file_cursor_line; - int accumulated_rows = calculate_wrapped_line_height(viewer->file_lines[viewer->file_cursor_line].line, width - 4); - - while (new_scroll_offset > viewer->file_scroll_offset && accumulated_rows > target_remaining_rows) { + int accumulated_rows = calculate_wrapped_line_height( + viewer->file_lines[viewer->file_cursor_line].line, width - 4); + + while (new_scroll_offset > viewer->file_scroll_offset && + accumulated_rows > target_remaining_rows) { new_scroll_offset--; - accumulated_rows -= calculate_wrapped_line_height(viewer->file_lines[new_scroll_offset].line, width - 4); + accumulated_rows -= calculate_wrapped_line_height( + viewer->file_lines[new_scroll_offset].line, width - 4); } - + if (new_scroll_offset > viewer->file_scroll_offset) { viewer->file_scroll_offset = new_scroll_offset; } - + int max_scroll = viewer->file_line_count - 1; if (viewer->file_scroll_offset > max_scroll) { viewer->file_scroll_offset = max_scroll; @@ -6589,30 +6622,35 @@ void move_cursor_smart_staged(NCursesDiffViewer *viewer, int direction) { getmaxyx(viewer->file_content_win, height, width); int split_line = height / 2; int staged_height = height - split_line - 2; - + if (direction == -1) { if (viewer->staged_cursor_line > 0) { viewer->staged_cursor_line--; - + // Calculate display positions accounting for wrapped lines int cursor_display_rows = 0; - + // Count display rows from scroll offset to cursor - for (int i = viewer->staged_scroll_offset; i < viewer->staged_cursor_line && i < viewer->staged_line_count; i++) { - cursor_display_rows += calculate_wrapped_line_height(viewer->staged_lines[i].line, width - 4); + for (int i = viewer->staged_scroll_offset; + i < viewer->staged_cursor_line && i < viewer->staged_line_count; + i++) { + cursor_display_rows += calculate_wrapped_line_height( + viewer->staged_lines[i].line, width - 4); } - + if (cursor_display_rows < 1) { // Need to scroll up to make cursor visible int target_rows = 1; int new_scroll_offset = viewer->staged_cursor_line; - int accumulated_rows = calculate_wrapped_line_height(viewer->staged_lines[viewer->staged_cursor_line].line, width - 4); - + int accumulated_rows = calculate_wrapped_line_height( + viewer->staged_lines[viewer->staged_cursor_line].line, width - 4); + while (new_scroll_offset > 0 && accumulated_rows < target_rows) { new_scroll_offset--; - accumulated_rows += calculate_wrapped_line_height(viewer->staged_lines[new_scroll_offset].line, width - 4); + accumulated_rows += calculate_wrapped_line_height( + viewer->staged_lines[new_scroll_offset].line, width - 4); } - + viewer->staged_scroll_offset = new_scroll_offset; if (viewer->staged_scroll_offset < 0) { viewer->staged_scroll_offset = 0; @@ -6622,30 +6660,36 @@ void move_cursor_smart_staged(NCursesDiffViewer *viewer, int direction) { } else { if (viewer->staged_cursor_line < viewer->staged_line_count - 1) { viewer->staged_cursor_line++; - + // Calculate display positions accounting for wrapped lines int cursor_display_rows = 0; - + // Count display rows from scroll offset to cursor - for (int i = viewer->staged_scroll_offset; i < viewer->staged_cursor_line && i < viewer->staged_line_count; i++) { - cursor_display_rows += calculate_wrapped_line_height(viewer->staged_lines[i].line, width - 4); + for (int i = viewer->staged_scroll_offset; + i < viewer->staged_cursor_line && i < viewer->staged_line_count; + i++) { + cursor_display_rows += calculate_wrapped_line_height( + viewer->staged_lines[i].line, width - 4); } - + if (cursor_display_rows >= staged_height - 1) { // Need to scroll down to make cursor visible int target_remaining_rows = staged_height - 2; int new_scroll_offset = viewer->staged_cursor_line; - int accumulated_rows = calculate_wrapped_line_height(viewer->staged_lines[viewer->staged_cursor_line].line, width - 4); - - while (new_scroll_offset > viewer->staged_scroll_offset && accumulated_rows > target_remaining_rows) { + int accumulated_rows = calculate_wrapped_line_height( + viewer->staged_lines[viewer->staged_cursor_line].line, width - 4); + + while (new_scroll_offset > viewer->staged_scroll_offset && + accumulated_rows > target_remaining_rows) { new_scroll_offset--; - accumulated_rows -= calculate_wrapped_line_height(viewer->staged_lines[new_scroll_offset].line, width - 4); + accumulated_rows -= calculate_wrapped_line_height( + viewer->staged_lines[new_scroll_offset].line, width - 4); } - + if (new_scroll_offset > viewer->staged_scroll_offset) { viewer->staged_scroll_offset = new_scroll_offset; } - + int max_scroll = viewer->staged_line_count - 1; if (viewer->staged_scroll_offset > max_scroll) { viewer->staged_scroll_offset = max_scroll; @@ -6684,10 +6728,10 @@ void cleanup_ncurses_diff_viewer(NCursesDiffViewer *viewer) { if (viewer->status_bar_win) { delwin(viewer->status_bar_win); } - + // Clean up fuzzy search windows cleanup_fuzzy_search(viewer); - + // Clean up grep search windows cleanup_grep_search(viewer); } @@ -6698,8 +6742,9 @@ void cleanup_ncurses_diff_viewer(NCursesDiffViewer *viewer) { * Initialize fuzzy search state */ void init_fuzzy_search(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->fuzzy_search_active = 0; viewer->fuzzy_search_query[0] = '\0'; viewer->fuzzy_search_query_len = 0; @@ -6708,7 +6753,7 @@ void init_fuzzy_search(NCursesDiffViewer *viewer) { viewer->fuzzy_scroll_offset = 0; viewer->fuzzy_input_win = NULL; viewer->fuzzy_list_win = NULL; - + // Initialize state tracking viewer->fuzzy_needs_full_redraw = 0; viewer->fuzzy_needs_input_redraw = 0; @@ -6723,8 +6768,9 @@ void init_fuzzy_search(NCursesDiffViewer *viewer) { * Cleanup fuzzy search windows */ void cleanup_fuzzy_search(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + if (viewer->fuzzy_input_win) { delwin(viewer->fuzzy_input_win); viewer->fuzzy_input_win = NULL; @@ -6740,36 +6786,40 @@ void cleanup_fuzzy_search(NCursesDiffViewer *viewer) { * Returns 0 if no match, positive score if match */ int calculate_fuzzy_score(const char *pattern, const char *filename) { - if (!pattern || !filename) return 0; - if (strlen(pattern) == 0) return 1000; // Empty pattern matches everything with high score - + if (!pattern || !filename) + return 0; + if (strlen(pattern) == 0) + return 1000; // Empty pattern matches everything with high score + int pattern_len = strlen(pattern); int filename_len = strlen(filename); - - if (pattern_len > filename_len) return 0; // Pattern longer than filename - + + if (pattern_len > filename_len) + return 0; // Pattern longer than filename + int score = 0; int pattern_pos = 0; int consecutive_matches = 0; int first_char_bonus = 0; - + for (int i = 0; i < filename_len && pattern_pos < pattern_len; i++) { char p_char = tolower(pattern[pattern_pos]); char f_char = tolower(filename[i]); - + if (p_char == f_char) { // Base score for character match score += 1; - + // Bonus for consecutive matches (prioritizes contiguous substrings) consecutive_matches++; score += consecutive_matches * 5; - + // Bonus for matching at word boundaries - if (i == 0 || filename[i-1] == '/' || filename[i-1] == '_' || filename[i-1] == '-' || filename[i-1] == '.') { + if (i == 0 || filename[i - 1] == '/' || filename[i - 1] == '_' || + filename[i - 1] == '-' || filename[i - 1] == '.') { score += 15; } - + // Bonus for matching first character if (pattern_pos == 0) { if (i == 0) { @@ -6783,32 +6833,33 @@ int calculate_fuzzy_score(const char *pattern, const char *filename) { } } } - + // Bonus for exact position match (same position in pattern and filename) if (i == pattern_pos) { score += 10; } - + pattern_pos++; } else { // Reset consecutive matches consecutive_matches = 0; } } - + // Must match all pattern characters - if (pattern_pos < pattern_len) return 0; - + if (pattern_pos < pattern_len) + return 0; + // Apply first character bonus score += first_char_bonus; - + // Bonus for shorter filenames (prefer more specific matches) score += (100 - filename_len); - + // Bonus for fewer unmatched characters int unmatched_chars = filename_len - pattern_len; score += (50 - unmatched_chars); - + return score; } @@ -6816,34 +6867,45 @@ int calculate_fuzzy_score(const char *pattern, const char *filename) { * Compare function for sorting scored files (higher scores first) */ int compare_scored_files(const void *a, const void *b) { - const struct {int file_index; int score;} *file_a = a; - const struct {int file_index; int score;} *file_b = b; - return file_b->score - file_a->score; // Descending order (higher scores first) + const struct { + int file_index; + int score; + } *file_a = a; + const struct { + int file_index; + int score; + } *file_b = b; + return file_b->score - + file_a->score; // Descending order (higher scores first) } /** * Update fuzzy search filter based on current query */ void update_fuzzy_filter(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->fuzzy_filtered_count = 0; viewer->fuzzy_selected_index = 0; viewer->fuzzy_scroll_offset = 0; - + // Calculate scores for all files and collect matches - for (int i = 0; i < viewer->file_count && viewer->fuzzy_filtered_count < MAX_FILES; i++) { - int score = calculate_fuzzy_score(viewer->fuzzy_search_query, viewer->files[i].filename); + for (int i = 0; + i < viewer->file_count && viewer->fuzzy_filtered_count < MAX_FILES; + i++) { + int score = calculate_fuzzy_score(viewer->fuzzy_search_query, + viewer->files[i].filename); if (score > 0) { viewer->fuzzy_scored_files[viewer->fuzzy_filtered_count].file_index = i; viewer->fuzzy_scored_files[viewer->fuzzy_filtered_count].score = score; viewer->fuzzy_filtered_count++; } } - + // Sort results by score (highest first) if (viewer->fuzzy_filtered_count > 1) { - qsort(viewer->fuzzy_scored_files, viewer->fuzzy_filtered_count, + qsort(viewer->fuzzy_scored_files, viewer->fuzzy_filtered_count, sizeof(viewer->fuzzy_scored_files[0]), compare_scored_files); } } @@ -6852,34 +6914,36 @@ void update_fuzzy_filter(NCursesDiffViewer *viewer) { * Enter fuzzy search mode */ void enter_fuzzy_search_mode(NCursesDiffViewer *viewer) { - if (!viewer || viewer->current_mode != NCURSES_MODE_FILE_LIST) return; - + if (!viewer || viewer->current_mode != NCURSES_MODE_FILE_LIST) + return; + viewer->fuzzy_search_active = 1; viewer->fuzzy_search_query[0] = '\0'; viewer->fuzzy_search_query_len = 0; - + // Create fuzzy search windows int input_height = 3; int list_height = viewer->terminal_height - input_height - 4; int width = viewer->terminal_width - 4; int start_y = (viewer->terminal_height - input_height - list_height) / 2; int start_x = 2; - + viewer->fuzzy_input_win = newwin(input_height, width, start_y, start_x); - viewer->fuzzy_list_win = newwin(list_height, width, start_y + input_height, start_x); - + viewer->fuzzy_list_win = + newwin(list_height, width, start_y + input_height, start_x); + if (viewer->fuzzy_input_win) { box(viewer->fuzzy_input_win, 0, 0); mvwprintw(viewer->fuzzy_input_win, 0, 2, " Fuzzy File Search "); } - + if (viewer->fuzzy_list_win) { box(viewer->fuzzy_list_win, 0, 0); } - + // Initialize with all files update_fuzzy_filter(viewer); - + // Force initial full redraw viewer->fuzzy_needs_full_redraw = 1; } @@ -6888,11 +6952,12 @@ void enter_fuzzy_search_mode(NCursesDiffViewer *viewer) { * Exit fuzzy search mode */ void exit_fuzzy_search_mode(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->fuzzy_search_active = 0; cleanup_fuzzy_search(viewer); - + // Force redraw of main windows next time touchwin(stdscr); refresh(); @@ -6902,24 +6967,25 @@ void exit_fuzzy_search_mode(NCursesDiffViewer *viewer) { * Render fuzzy search input field only */ void render_fuzzy_input(NCursesDiffViewer *viewer) { - if (!viewer->fuzzy_input_win) return; - + if (!viewer->fuzzy_input_win) + return; + // Clear only the content area, not the border for (int y = 1; y < getmaxy(viewer->fuzzy_input_win) - 1; y++) { for (int x = 1; x < getmaxx(viewer->fuzzy_input_win) - 1; x++) { mvwaddch(viewer->fuzzy_input_win, y, x, ' '); } } - + // Show current query mvwprintw(viewer->fuzzy_input_win, 1, 2, "> %s", viewer->fuzzy_search_query); - + // Show cursor int cursor_x = 4 + viewer->fuzzy_search_query_len; if (cursor_x < getmaxx(viewer->fuzzy_input_win) - 1) { mvwaddch(viewer->fuzzy_input_win, 1, cursor_x, '_'); } - + wrefresh(viewer->fuzzy_input_win); } @@ -6927,49 +6993,53 @@ void render_fuzzy_input(NCursesDiffViewer *viewer) { * Render fuzzy search file list content only (no borders) */ void render_fuzzy_list_content(NCursesDiffViewer *viewer) { - if (!viewer->fuzzy_list_win) return; - + if (!viewer->fuzzy_list_win) + return; + int list_height = getmaxy(viewer->fuzzy_list_win) - 2; int list_width = getmaxx(viewer->fuzzy_list_win) - 4; - + // Clear only the content area, not the border for (int y = 1; y <= list_height; y++) { for (int x = 1; x < getmaxx(viewer->fuzzy_list_win) - 1; x++) { mvwaddch(viewer->fuzzy_list_win, y, x, ' '); } } - + // Show filtered results for (int i = 0; i < viewer->fuzzy_filtered_count && i < list_height; i++) { int display_index = i + viewer->fuzzy_scroll_offset; - if (display_index >= viewer->fuzzy_filtered_count) break; - + if (display_index >= viewer->fuzzy_filtered_count) + break; + int file_index = viewer->fuzzy_scored_files[display_index].file_index; char *filename = viewer->files[file_index].filename; char status = viewer->files[file_index].status; - + // Highlight selected item if (display_index == viewer->fuzzy_selected_index) { wattron(viewer->fuzzy_list_win, A_REVERSE); } - + // Show status character and filename - mvwprintw(viewer->fuzzy_list_win, i + 1, 2, "%c %-*.*s", - status, list_width - 3, list_width - 3, filename); - + mvwprintw(viewer->fuzzy_list_win, i + 1, 2, "%c %-*.*s", status, + list_width - 3, list_width - 3, filename); + if (display_index == viewer->fuzzy_selected_index) { wattroff(viewer->fuzzy_list_win, A_REVERSE); } } - + // Show result count in top border area if (viewer->fuzzy_filtered_count > 0) { - mvwprintw(viewer->fuzzy_list_win, 0, getmaxx(viewer->fuzzy_list_win) - 15, - " %d/%d ", viewer->fuzzy_selected_index + 1, viewer->fuzzy_filtered_count); + mvwprintw(viewer->fuzzy_list_win, 0, getmaxx(viewer->fuzzy_list_win) - 15, + " %d/%d ", viewer->fuzzy_selected_index + 1, + viewer->fuzzy_filtered_count); } else { - mvwprintw(viewer->fuzzy_list_win, 0, getmaxx(viewer->fuzzy_list_win) - 10, " 0/0 "); + mvwprintw(viewer->fuzzy_list_win, 0, getmaxx(viewer->fuzzy_list_win) - 10, + " 0/0 "); } - + wrefresh(viewer->fuzzy_list_win); } @@ -6977,14 +7047,15 @@ void render_fuzzy_list_content(NCursesDiffViewer *viewer) { * Create fuzzy search windows with borders (one-time setup) */ void create_fuzzy_windows_with_borders(NCursesDiffViewer *viewer) { - if (!viewer->fuzzy_input_win || !viewer->fuzzy_list_win) return; - + if (!viewer->fuzzy_input_win || !viewer->fuzzy_list_win) + return; + // Draw input window border and title wclear(viewer->fuzzy_input_win); box(viewer->fuzzy_input_win, 0, 0); mvwprintw(viewer->fuzzy_input_win, 0, 2, " Fuzzy File Search "); wrefresh(viewer->fuzzy_input_win); - + // Draw list window border wclear(viewer->fuzzy_list_win); box(viewer->fuzzy_list_win, 0, 0); @@ -6995,15 +7066,20 @@ void create_fuzzy_windows_with_borders(NCursesDiffViewer *viewer) { * Main fuzzy search render function with granular updates */ void render_fuzzy_search(NCursesDiffViewer *viewer) { - if (!viewer || !viewer->fuzzy_search_active) return; - if (!viewer->fuzzy_input_win || !viewer->fuzzy_list_win) return; - + if (!viewer || !viewer->fuzzy_search_active) + return; + if (!viewer->fuzzy_input_win || !viewer->fuzzy_list_win) + return; + // Check what specifically needs to be redrawn - int query_changed = strcmp(viewer->fuzzy_search_query, viewer->fuzzy_last_query) != 0; - int selection_changed = viewer->fuzzy_selected_index != viewer->fuzzy_last_selected; + int query_changed = + strcmp(viewer->fuzzy_search_query, viewer->fuzzy_last_query) != 0; + int selection_changed = + viewer->fuzzy_selected_index != viewer->fuzzy_last_selected; int scroll_changed = viewer->fuzzy_scroll_offset != viewer->fuzzy_last_scroll; - int results_changed = viewer->fuzzy_filtered_count != viewer->fuzzy_last_filtered_count; - + int results_changed = + viewer->fuzzy_filtered_count != viewer->fuzzy_last_filtered_count; + // Full redraw needed on first render if (viewer->fuzzy_needs_full_redraw) { create_fuzzy_windows_with_borders(viewer); @@ -7016,13 +7092,14 @@ void render_fuzzy_search(NCursesDiffViewer *viewer) { render_fuzzy_input(viewer); viewer->fuzzy_needs_input_redraw = 0; } - + // Redraw file list if results, selection, or scroll changed - if (results_changed || selection_changed || scroll_changed || viewer->fuzzy_needs_list_redraw) { + if (results_changed || selection_changed || scroll_changed || + viewer->fuzzy_needs_list_redraw) { render_fuzzy_list_content(viewer); viewer->fuzzy_needs_list_redraw = 0; } - + // Update last rendered state strcpy(viewer->fuzzy_last_query, viewer->fuzzy_search_query); viewer->fuzzy_last_selected = viewer->fuzzy_selected_index; @@ -7034,61 +7111,64 @@ void render_fuzzy_search(NCursesDiffViewer *viewer) { * Handle fuzzy search input */ int handle_fuzzy_search_input(NCursesDiffViewer *viewer, int key) { - if (!viewer || !viewer->fuzzy_search_active) return 0; - + if (!viewer || !viewer->fuzzy_search_active) + return 0; + switch (key) { - case 27: // ESC + case 27: // ESC + exit_fuzzy_search_mode(viewer); + return 1; + + case KEY_ENTER: + case '\n': + case '\r': + if (viewer->fuzzy_filtered_count > 0) { + select_fuzzy_file(viewer); exit_fuzzy_search_mode(viewer); - return 1; - - case KEY_ENTER: - case '\n': - case '\r': - if (viewer->fuzzy_filtered_count > 0) { - select_fuzzy_file(viewer); - exit_fuzzy_search_mode(viewer); + } + return 1; + + case KEY_UP: + if (viewer->fuzzy_selected_index > 0) { + viewer->fuzzy_selected_index--; + // Adjust scroll if needed + if (viewer->fuzzy_selected_index < viewer->fuzzy_scroll_offset) { + viewer->fuzzy_scroll_offset = viewer->fuzzy_selected_index; } - return 1; - - case KEY_UP: - if (viewer->fuzzy_selected_index > 0) { - viewer->fuzzy_selected_index--; - // Adjust scroll if needed - if (viewer->fuzzy_selected_index < viewer->fuzzy_scroll_offset) { - viewer->fuzzy_scroll_offset = viewer->fuzzy_selected_index; - } + } + return 1; + + case KEY_DOWN: + if (viewer->fuzzy_selected_index < viewer->fuzzy_filtered_count - 1) { + viewer->fuzzy_selected_index++; + // Adjust scroll if needed + int list_height = getmaxy(viewer->fuzzy_list_win) - 2; + if (viewer->fuzzy_selected_index >= + viewer->fuzzy_scroll_offset + list_height) { + viewer->fuzzy_scroll_offset = + viewer->fuzzy_selected_index - list_height + 1; } - return 1; - - case KEY_DOWN: - if (viewer->fuzzy_selected_index < viewer->fuzzy_filtered_count - 1) { - viewer->fuzzy_selected_index++; - // Adjust scroll if needed - int list_height = getmaxy(viewer->fuzzy_list_win) - 2; - if (viewer->fuzzy_selected_index >= viewer->fuzzy_scroll_offset + list_height) { - viewer->fuzzy_scroll_offset = viewer->fuzzy_selected_index - list_height + 1; - } - } - return 1; - - case KEY_BACKSPACE: - case 127: - case '\b': - if (viewer->fuzzy_search_query_len > 0) { - viewer->fuzzy_search_query_len--; - viewer->fuzzy_search_query[viewer->fuzzy_search_query_len] = '\0'; - update_fuzzy_filter(viewer); - } - return 1; - - default: - // Add printable characters to search query - if (key >= 32 && key <= 126 && viewer->fuzzy_search_query_len < 255) { - viewer->fuzzy_search_query[viewer->fuzzy_search_query_len++] = key; - viewer->fuzzy_search_query[viewer->fuzzy_search_query_len] = '\0'; - update_fuzzy_filter(viewer); - } - return 1; + } + return 1; + + case KEY_BACKSPACE: + case 127: + case '\b': + if (viewer->fuzzy_search_query_len > 0) { + viewer->fuzzy_search_query_len--; + viewer->fuzzy_search_query[viewer->fuzzy_search_query_len] = '\0'; + update_fuzzy_filter(viewer); + } + return 1; + + default: + // Add printable characters to search query + if (key >= 32 && key <= 126 && viewer->fuzzy_search_query_len < 255) { + viewer->fuzzy_search_query[viewer->fuzzy_search_query_len++] = key; + viewer->fuzzy_search_query[viewer->fuzzy_search_query_len] = '\0'; + update_fuzzy_filter(viewer); + } + return 1; } } @@ -7096,14 +7176,16 @@ int handle_fuzzy_search_input(NCursesDiffViewer *viewer, int key) { * Select file from fuzzy search results */ void select_fuzzy_file(NCursesDiffViewer *viewer) { - if (!viewer || viewer->fuzzy_filtered_count == 0) return; - + if (!viewer || viewer->fuzzy_filtered_count == 0) + return; + // Get the actual file index from scored results - int file_index = viewer->fuzzy_scored_files[viewer->fuzzy_selected_index].file_index; - + int file_index = + viewer->fuzzy_scored_files[viewer->fuzzy_selected_index].file_index; + // Update the main file list selection to point to this file viewer->selected_file = file_index; - + // Scroll the main file list to show the selected file int file_panel_height = getmaxy(viewer->file_list_win) - 2; if (viewer->selected_file < viewer->file_count) { @@ -7111,26 +7193,29 @@ void select_fuzzy_file(NCursesDiffViewer *viewer) { if (viewer->selected_file < file_panel_height / 2) { // If near the top, scroll to top // No action needed as the file list starts from 0 - } else if (viewer->selected_file >= viewer->file_count - file_panel_height / 2) { + } else if (viewer->selected_file >= + viewer->file_count - file_panel_height / 2) { // If near the bottom, show as much as possible // No action needed as we'll handle this in rendering } } - + // Load the selected file with split staging view (same as pressing Enter) - if (viewer->selected_file >= 0 && viewer->selected_file < viewer->file_count) { + if (viewer->selected_file >= 0 && + viewer->selected_file < viewer->file_count) { const char *filename = viewer->files[viewer->selected_file].filename; - + // Load file with staging information load_file_with_staging_info(viewer, filename); - + // Switch to split view mode viewer->split_view_mode = 1; viewer->current_mode = NCURSES_MODE_FILE_VIEW; viewer->active_pane = 0; // Start with unstaged pane active - + // Store current file path - strncpy(viewer->current_file_path, filename, sizeof(viewer->current_file_path) - 1); + strncpy(viewer->current_file_path, filename, + sizeof(viewer->current_file_path) - 1); viewer->current_file_path[sizeof(viewer->current_file_path) - 1] = '\0'; } } @@ -7139,8 +7224,9 @@ void select_fuzzy_file(NCursesDiffViewer *viewer) { * Initialize grep search state */ void init_grep_search(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->grep_search_active = 0; viewer->grep_search_mode = NCURSES_MODE_FILE_LIST; viewer->grep_search_query[0] = '\0'; @@ -7150,7 +7236,7 @@ void init_grep_search(NCursesDiffViewer *viewer) { viewer->grep_scroll_offset = 0; viewer->grep_input_win = NULL; viewer->grep_list_win = NULL; - + // Initialize state tracking viewer->grep_needs_full_redraw = 0; viewer->grep_needs_input_redraw = 0; @@ -7165,8 +7251,9 @@ void init_grep_search(NCursesDiffViewer *viewer) { * Cleanup grep search windows */ void cleanup_grep_search(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + if (viewer->grep_input_win) { delwin(viewer->grep_input_win); viewer->grep_input_win = NULL; @@ -7180,25 +7267,27 @@ void cleanup_grep_search(NCursesDiffViewer *viewer) { /** * Extract branch name from stash info */ -void extract_branch_from_stash(const char *stash_info, char *branch_name, int max_len) { - if (!stash_info || !branch_name || max_len <= 0) return; - +void extract_branch_from_stash(const char *stash_info, char *branch_name, + int max_len) { + if (!stash_info || !branch_name || max_len <= 0) + return; + branch_name[0] = '\0'; - + // Look for "On " pattern in stash info const char *on_pattern = "On "; const char *on_pos = strstr(stash_info, on_pattern); - + if (on_pos) { const char *branch_start = on_pos + strlen(on_pattern); const char *branch_end = strchr(branch_start, ':'); - + if (branch_end && branch_end > branch_start) { int branch_len = branch_end - branch_start; if (branch_len < max_len) { strncpy(branch_name, branch_start, branch_len); branch_name[branch_len] = '\0'; - + // Trim trailing spaces while (branch_len > 0 && branch_name[branch_len - 1] == ' ') { branch_name[--branch_len] = '\0'; @@ -7212,59 +7301,64 @@ void extract_branch_from_stash(const char *stash_info, char *branch_name, int ma * Calculate grep match score for text content */ int calculate_grep_score(const char *pattern, const char *text) { - if (!pattern || !text) return 0; - if (strlen(pattern) == 0) return 1000; // Empty pattern matches everything - + if (!pattern || !text) + return 0; + if (strlen(pattern) == 0) + return 1000; // Empty pattern matches everything + int pattern_len = strlen(pattern); int text_len = strlen(text); - - if (pattern_len > text_len) return 0; - + + if (pattern_len > text_len) + return 0; + int score = 0; int pattern_pos = 0; int consecutive_matches = 0; int first_char_bonus = 0; - + for (int i = 0; i < text_len && pattern_pos < pattern_len; i++) { char p_char = tolower(pattern[pattern_pos]); char t_char = tolower(text[i]); - + if (p_char == t_char) { // Base score for character match score += 1; - + // Bonus for consecutive matches consecutive_matches++; score += consecutive_matches * 3; - + // Bonus for matching at word boundaries - if (i == 0 || text[i-1] == ' ' || text[i-1] == '-' || text[i-1] == '_') { + if (i == 0 || text[i - 1] == ' ' || text[i - 1] == '-' || + text[i - 1] == '_') { score += 10; } - + // Bonus for matching first character if (pattern_pos == 0 && i == 0) { first_char_bonus = 30; } - + // Bonus for exact position match if (i == pattern_pos) { score += 5; } - + pattern_pos++; } else { consecutive_matches = 0; } } - + // Must match all pattern characters - if (pattern_pos < pattern_len) return 0; - + if (pattern_pos < pattern_len) + return 0; + score += first_char_bonus; - score += (50 - text_len); // Prefer shorter text + score += (50 - text_len); // Prefer shorter text score += (30 - (text_len - pattern_len)); // Fewer unmatched chars - + return score; } @@ -7272,8 +7366,14 @@ int calculate_grep_score(const char *pattern, const char *text) { * Compare function for sorting grep scored items */ int compare_grep_scored_items(const void *a, const void *b) { - const struct {int item_index; int score;} *item_a = a; - const struct {int item_index; int score;} *item_b = b; + const struct { + int item_index; + int score; + } *item_a = a; + const struct { + int item_index; + int score; + } *item_b = b; return item_b->score - item_a->score; // Descending order } @@ -7281,76 +7381,90 @@ int compare_grep_scored_items(const void *a, const void *b) { * Update grep search filter based on current query and mode */ void update_grep_filter(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->grep_filtered_count = 0; viewer->grep_selected_index = 0; viewer->grep_scroll_offset = 0; - + // Search different data based on current mode switch (viewer->grep_search_mode) { - case NCURSES_MODE_COMMIT_LIST: - // Search commit titles and author initials - for (int i = 0; i < viewer->commit_count && viewer->grep_filtered_count < MAX_COMMITS; i++) { - // Try matching against title first - int title_score = calculate_grep_score(viewer->grep_search_query, viewer->commits[i].title); - - // Try matching against author initials - int author_score = calculate_grep_score(viewer->grep_search_query, viewer->commits[i].author_initials); - - // Use the higher score (prefer title matches over author matches) - int score = (title_score > author_score) ? title_score : author_score; - - if (score > 0) { - viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; - viewer->grep_scored_items[viewer->grep_filtered_count].score = score; - viewer->grep_filtered_count++; - } + case NCURSES_MODE_COMMIT_LIST: + // Search commit titles and author initials + for (int i = 0; + i < viewer->commit_count && viewer->grep_filtered_count < MAX_COMMITS; + i++) { + // Try matching against title first + int title_score = calculate_grep_score(viewer->grep_search_query, + viewer->commits[i].title); + + // Try matching against author initials + int author_score = calculate_grep_score( + viewer->grep_search_query, viewer->commits[i].author_initials); + + // Use the higher score (prefer title matches over author matches) + int score = (title_score > author_score) ? title_score : author_score; + + if (score > 0) { + viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; + viewer->grep_scored_items[viewer->grep_filtered_count].score = score; + viewer->grep_filtered_count++; } - break; - - case NCURSES_MODE_STASH_LIST: - // Search stash info and branch names - for (int i = 0; i < viewer->stash_count && viewer->grep_filtered_count < MAX_COMMITS; i++) { - // Try matching against full stash info first - int stash_score = calculate_grep_score(viewer->grep_search_query, viewer->stashes[i].stash_info); - - // Extract and try matching against branch name - char branch_name[64]; - extract_branch_from_stash(viewer->stashes[i].stash_info, branch_name, sizeof(branch_name)); - int branch_score = 0; - if (strlen(branch_name) > 0) { - branch_score = calculate_grep_score(viewer->grep_search_query, branch_name); - } - - // Use the higher score (prefer full stash info matches over branch-only matches) - int score = (stash_score > branch_score) ? stash_score : branch_score; - - if (score > 0) { - viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; - viewer->grep_scored_items[viewer->grep_filtered_count].score = score; - viewer->grep_filtered_count++; - } + } + break; + + case NCURSES_MODE_STASH_LIST: + // Search stash info and branch names + for (int i = 0; + i < viewer->stash_count && viewer->grep_filtered_count < MAX_COMMITS; + i++) { + // Try matching against full stash info first + int stash_score = calculate_grep_score(viewer->grep_search_query, + viewer->stashes[i].stash_info); + + // Extract and try matching against branch name + char branch_name[64]; + extract_branch_from_stash(viewer->stashes[i].stash_info, branch_name, + sizeof(branch_name)); + int branch_score = 0; + if (strlen(branch_name) > 0) { + branch_score = + calculate_grep_score(viewer->grep_search_query, branch_name); } - break; - - case NCURSES_MODE_BRANCH_LIST: - // Search branch names - for (int i = 0; i < viewer->branch_count && viewer->grep_filtered_count < MAX_COMMITS; i++) { - int score = calculate_grep_score(viewer->grep_search_query, viewer->branches[i].name); - if (score > 0) { - viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; - viewer->grep_scored_items[viewer->grep_filtered_count].score = score; - viewer->grep_filtered_count++; - } + + // Use the higher score (prefer full stash info matches over branch-only + // matches) + int score = (stash_score > branch_score) ? stash_score : branch_score; + + if (score > 0) { + viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; + viewer->grep_scored_items[viewer->grep_filtered_count].score = score; + viewer->grep_filtered_count++; } - break; - - default: - // No grep search for other modes - break; + } + break; + + case NCURSES_MODE_BRANCH_LIST: + // Search branch names + for (int i = 0; + i < viewer->branch_count && viewer->grep_filtered_count < MAX_COMMITS; + i++) { + int score = calculate_grep_score(viewer->grep_search_query, + viewer->branches[i].name); + if (score > 0) { + viewer->grep_scored_items[viewer->grep_filtered_count].item_index = i; + viewer->grep_scored_items[viewer->grep_filtered_count].score = score; + viewer->grep_filtered_count++; + } + } + break; + + default: + // No grep search for other modes + break; } - + // Sort results by score if (viewer->grep_filtered_count > 1) { qsort(viewer->grep_scored_items, viewer->grep_filtered_count, @@ -7362,33 +7476,35 @@ void update_grep_filter(NCursesDiffViewer *viewer) { * Enter grep search mode for current view mode */ void enter_grep_search_mode(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + // Only allow grep search in specific modes if (viewer->current_mode != NCURSES_MODE_COMMIT_LIST && viewer->current_mode != NCURSES_MODE_STASH_LIST && viewer->current_mode != NCURSES_MODE_BRANCH_LIST) { return; } - + viewer->grep_search_active = 1; viewer->grep_search_mode = viewer->current_mode; viewer->grep_search_query[0] = '\0'; viewer->grep_search_query_len = 0; - + // Create grep search windows int input_height = 3; int list_height = viewer->terminal_height - input_height - 4; int width = viewer->terminal_width - 4; int start_y = (viewer->terminal_height - input_height - list_height) / 2; int start_x = 2; - + viewer->grep_input_win = newwin(input_height, width, start_y, start_x); - viewer->grep_list_win = newwin(list_height, width, start_y + input_height, start_x); - + viewer->grep_list_win = + newwin(list_height, width, start_y + input_height, start_x); + // Initialize with all items update_grep_filter(viewer); - + // Force initial full redraw viewer->grep_needs_full_redraw = 1; } @@ -7397,11 +7513,12 @@ void enter_grep_search_mode(NCursesDiffViewer *viewer) { * Exit grep search mode */ void exit_grep_search_mode(NCursesDiffViewer *viewer) { - if (!viewer) return; - + if (!viewer) + return; + viewer->grep_search_active = 0; cleanup_grep_search(viewer); - + // Force redraw of main windows next time touchwin(stdscr); refresh(); @@ -7411,24 +7528,25 @@ void exit_grep_search_mode(NCursesDiffViewer *viewer) { * Render grep search input field only */ void render_grep_input(NCursesDiffViewer *viewer) { - if (!viewer->grep_input_win) return; - + if (!viewer->grep_input_win) + return; + // Clear only the content area, not the border for (int y = 1; y < getmaxy(viewer->grep_input_win) - 1; y++) { for (int x = 1; x < getmaxx(viewer->grep_input_win) - 1; x++) { mvwaddch(viewer->grep_input_win, y, x, ' '); } } - + // Show current query mvwprintw(viewer->grep_input_win, 1, 2, "> %s", viewer->grep_search_query); - + // Show cursor int cursor_x = 4 + viewer->grep_search_query_len; if (cursor_x < getmaxx(viewer->grep_input_win) - 1) { mvwaddch(viewer->grep_input_win, 1, cursor_x, '_'); } - + wrefresh(viewer->grep_input_win); } @@ -7436,133 +7554,143 @@ void render_grep_input(NCursesDiffViewer *viewer) { * Render grep search list content only (no borders) */ void render_grep_list_content(NCursesDiffViewer *viewer) { - if (!viewer->grep_list_win) return; - + if (!viewer->grep_list_win) + return; + int list_height = getmaxy(viewer->grep_list_win) - 2; int list_width = getmaxx(viewer->grep_list_win) - 4; - + // Clear only the content area, not the border for (int y = 1; y <= list_height; y++) { for (int x = 1; x < getmaxx(viewer->grep_list_win) - 1; x++) { mvwaddch(viewer->grep_list_win, y, x, ' '); } } - + // Show filtered results for (int i = 0; i < viewer->grep_filtered_count && i < list_height; i++) { int display_index = i + viewer->grep_scroll_offset; - if (display_index >= viewer->grep_filtered_count) break; - + if (display_index >= viewer->grep_filtered_count) + break; + int item_index = viewer->grep_scored_items[display_index].item_index; char display_text[512] = ""; - + // Get display text based on mode switch (viewer->grep_search_mode) { - case NCURSES_MODE_COMMIT_LIST: - // For commits, we'll handle colors separately - break; - case NCURSES_MODE_STASH_LIST: - // For stashes, we'll handle special formatting to highlight branch - break; - case NCURSES_MODE_BRANCH_LIST: - strncpy(display_text, viewer->branches[item_index].name, sizeof(display_text) - 1); - break; - default: - strcpy(display_text, "Unknown"); - break; + case NCURSES_MODE_COMMIT_LIST: + // For commits, we'll handle colors separately + break; + case NCURSES_MODE_STASH_LIST: + // For stashes, we'll handle special formatting to highlight branch + break; + case NCURSES_MODE_BRANCH_LIST: + strncpy(display_text, viewer->branches[item_index].name, + sizeof(display_text) - 1); + break; + default: + strcpy(display_text, "Unknown"); + break; } display_text[sizeof(display_text) - 1] = '\0'; - + // Highlight selected item if (display_index == viewer->grep_selected_index) { wattron(viewer->grep_list_win, A_REVERSE); } - + // Render based on mode with appropriate colors if (viewer->grep_search_mode == NCURSES_MODE_COMMIT_LIST) { - // Render commit with colors: hash (yellow) + author initials (green) + title + // Render commit with colors: hash (yellow) + author initials (green) + + // title int x_pos = 2; - + // Hash in yellow (COLOR_PAIR(10)) wattron(viewer->grep_list_win, COLOR_PAIR(10)); - mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%s", viewer->commits[item_index].hash); + mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%s", + viewer->commits[item_index].hash); wattroff(viewer->grep_list_win, COLOR_PAIR(10)); x_pos += strlen(viewer->commits[item_index].hash) + 1; - + // Author initials in green (COLOR_PAIR(8)) wattron(viewer->grep_list_win, COLOR_PAIR(8)); - mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%s", viewer->commits[item_index].author_initials); + mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%s", + viewer->commits[item_index].author_initials); wattroff(viewer->grep_list_win, COLOR_PAIR(8)); x_pos += strlen(viewer->commits[item_index].author_initials) + 1; - + // Title in default color int remaining_width = list_width - (x_pos - 2); if (remaining_width > 0) { - mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%-*.*s", - remaining_width, remaining_width, viewer->commits[item_index].title); + mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%-*.*s", + remaining_width, remaining_width, + viewer->commits[item_index].title); } } else if (viewer->grep_search_mode == NCURSES_MODE_STASH_LIST) { // Render stash with branch name highlighted const char *stash_info = viewer->stashes[item_index].stash_info; char branch_name[64]; extract_branch_from_stash(stash_info, branch_name, sizeof(branch_name)); - + // Find the "On branch_name:" part to highlight char on_pattern[80]; snprintf(on_pattern, sizeof(on_pattern), "On %s:", branch_name); const char *on_pos = strstr(stash_info, on_pattern); - + if (on_pos && strlen(branch_name) > 0) { int x_pos = 2; - + // Render text before "On" int before_len = on_pos - stash_info; if (before_len > 0) { - mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%.*s", before_len, stash_info); + mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%.*s", before_len, + stash_info); x_pos += before_len; } - + // Render "On " in default color mvwprintw(viewer->grep_list_win, i + 1, x_pos, "On "); x_pos += 3; - + // Render branch name in green (COLOR_PAIR(8)) wattron(viewer->grep_list_win, COLOR_PAIR(8)); mvwprintw(viewer->grep_list_win, i + 1, x_pos, "%s", branch_name); wattroff(viewer->grep_list_win, COLOR_PAIR(8)); x_pos += strlen(branch_name); - + // Render remaining text after branch name const char *after_pos = on_pos + strlen(on_pattern); int remaining_width = list_width - (x_pos - 2); if (remaining_width > 0 && *after_pos) { - mvwprintw(viewer->grep_list_win, i + 1, x_pos, ":%-*.*s", + mvwprintw(viewer->grep_list_win, i + 1, x_pos, ":%-*.*s", remaining_width - 1, remaining_width - 1, after_pos); } } else { // Fallback: render normally if no branch pattern found - mvwprintw(viewer->grep_list_win, i + 1, 2, "%-*.*s", - list_width - 2, list_width - 2, stash_info); + mvwprintw(viewer->grep_list_win, i + 1, 2, "%-*.*s", list_width - 2, + list_width - 2, stash_info); } } else { // Show item text for other modes (branches) - mvwprintw(viewer->grep_list_win, i + 1, 2, "%-*.*s", - list_width - 2, list_width - 2, display_text); + mvwprintw(viewer->grep_list_win, i + 1, 2, "%-*.*s", list_width - 2, + list_width - 2, display_text); } - + if (display_index == viewer->grep_selected_index) { wattroff(viewer->grep_list_win, A_REVERSE); } } - + // Show result count if (viewer->grep_filtered_count > 0) { - mvwprintw(viewer->grep_list_win, 0, getmaxx(viewer->grep_list_win) - 15, - " %d/%d ", viewer->grep_selected_index + 1, viewer->grep_filtered_count); + mvwprintw(viewer->grep_list_win, 0, getmaxx(viewer->grep_list_win) - 15, + " %d/%d ", viewer->grep_selected_index + 1, + viewer->grep_filtered_count); } else { - mvwprintw(viewer->grep_list_win, 0, getmaxx(viewer->grep_list_win) - 10, " 0/0 "); + mvwprintw(viewer->grep_list_win, 0, getmaxx(viewer->grep_list_win) - 10, + " 0/0 "); } - + wrefresh(viewer->grep_list_win); } @@ -7570,30 +7698,31 @@ void render_grep_list_content(NCursesDiffViewer *viewer) { * Create grep search windows with borders (one-time setup) */ void create_grep_windows_with_borders(NCursesDiffViewer *viewer) { - if (!viewer->grep_input_win || !viewer->grep_list_win) return; - + if (!viewer->grep_input_win || !viewer->grep_list_win) + return; + // Get mode name for title const char *mode_name = "Search"; switch (viewer->grep_search_mode) { - case NCURSES_MODE_COMMIT_LIST: - mode_name = "Commit Grep"; - break; - case NCURSES_MODE_STASH_LIST: - mode_name = "Stash Grep"; - break; - case NCURSES_MODE_BRANCH_LIST: - mode_name = "Branch Grep"; - break; - default: - break; + case NCURSES_MODE_COMMIT_LIST: + mode_name = "Commit Grep"; + break; + case NCURSES_MODE_STASH_LIST: + mode_name = "Stash Grep"; + break; + case NCURSES_MODE_BRANCH_LIST: + mode_name = "Branch Grep"; + break; + default: + break; } - + // Draw input window border and title wclear(viewer->grep_input_win); box(viewer->grep_input_win, 0, 0); mvwprintw(viewer->grep_input_win, 0, 2, " %s ", mode_name); wrefresh(viewer->grep_input_win); - + // Draw list window border wclear(viewer->grep_list_win); box(viewer->grep_list_win, 0, 0); @@ -7604,15 +7733,20 @@ void create_grep_windows_with_borders(NCursesDiffViewer *viewer) { * Main grep search render function with granular updates */ void render_grep_search(NCursesDiffViewer *viewer) { - if (!viewer || !viewer->grep_search_active) return; - if (!viewer->grep_input_win || !viewer->grep_list_win) return; - + if (!viewer || !viewer->grep_search_active) + return; + if (!viewer->grep_input_win || !viewer->grep_list_win) + return; + // Check what specifically needs to be redrawn - int query_changed = strcmp(viewer->grep_search_query, viewer->grep_last_query) != 0; - int selection_changed = viewer->grep_selected_index != viewer->grep_last_selected; + int query_changed = + strcmp(viewer->grep_search_query, viewer->grep_last_query) != 0; + int selection_changed = + viewer->grep_selected_index != viewer->grep_last_selected; int scroll_changed = viewer->grep_scroll_offset != viewer->grep_last_scroll; - int results_changed = viewer->grep_filtered_count != viewer->grep_last_filtered_count; - + int results_changed = + viewer->grep_filtered_count != viewer->grep_last_filtered_count; + // Full redraw needed on first render if (viewer->grep_needs_full_redraw) { create_grep_windows_with_borders(viewer); @@ -7625,13 +7759,14 @@ void render_grep_search(NCursesDiffViewer *viewer) { render_grep_input(viewer); viewer->grep_needs_input_redraw = 0; } - + // Redraw list if results, selection, or scroll changed - if (results_changed || selection_changed || scroll_changed || viewer->grep_needs_list_redraw) { + if (results_changed || selection_changed || scroll_changed || + viewer->grep_needs_list_redraw) { render_grep_list_content(viewer); viewer->grep_needs_list_redraw = 0; } - + // Update last rendered state strcpy(viewer->grep_last_query, viewer->grep_search_query); viewer->grep_last_selected = viewer->grep_selected_index; @@ -7643,61 +7778,64 @@ void render_grep_search(NCursesDiffViewer *viewer) { * Handle grep search input */ int handle_grep_search_input(NCursesDiffViewer *viewer, int key) { - if (!viewer || !viewer->grep_search_active) return 0; - + if (!viewer || !viewer->grep_search_active) + return 0; + switch (key) { - case 27: // ESC + case 27: // ESC + exit_grep_search_mode(viewer); + return 1; + + case KEY_ENTER: + case '\n': + case '\r': + if (viewer->grep_filtered_count > 0) { + select_grep_item(viewer); exit_grep_search_mode(viewer); - return 1; - - case KEY_ENTER: - case '\n': - case '\r': - if (viewer->grep_filtered_count > 0) { - select_grep_item(viewer); - exit_grep_search_mode(viewer); + } + return 1; + + case KEY_UP: + if (viewer->grep_selected_index > 0) { + viewer->grep_selected_index--; + // Adjust scroll if needed + if (viewer->grep_selected_index < viewer->grep_scroll_offset) { + viewer->grep_scroll_offset = viewer->grep_selected_index; } - return 1; - - case KEY_UP: - if (viewer->grep_selected_index > 0) { - viewer->grep_selected_index--; - // Adjust scroll if needed - if (viewer->grep_selected_index < viewer->grep_scroll_offset) { - viewer->grep_scroll_offset = viewer->grep_selected_index; - } + } + return 1; + + case KEY_DOWN: + if (viewer->grep_selected_index < viewer->grep_filtered_count - 1) { + viewer->grep_selected_index++; + // Adjust scroll if needed + int list_height = getmaxy(viewer->grep_list_win) - 2; + if (viewer->grep_selected_index >= + viewer->grep_scroll_offset + list_height) { + viewer->grep_scroll_offset = + viewer->grep_selected_index - list_height + 1; } - return 1; - - case KEY_DOWN: - if (viewer->grep_selected_index < viewer->grep_filtered_count - 1) { - viewer->grep_selected_index++; - // Adjust scroll if needed - int list_height = getmaxy(viewer->grep_list_win) - 2; - if (viewer->grep_selected_index >= viewer->grep_scroll_offset + list_height) { - viewer->grep_scroll_offset = viewer->grep_selected_index - list_height + 1; - } - } - return 1; - - case KEY_BACKSPACE: - case 127: - case '\b': - if (viewer->grep_search_query_len > 0) { - viewer->grep_search_query_len--; - viewer->grep_search_query[viewer->grep_search_query_len] = '\0'; - update_grep_filter(viewer); - } - return 1; - - default: - // Add printable characters to search query - if (key >= 32 && key <= 126 && viewer->grep_search_query_len < 255) { - viewer->grep_search_query[viewer->grep_search_query_len++] = key; - viewer->grep_search_query[viewer->grep_search_query_len] = '\0'; - update_grep_filter(viewer); - } - return 1; + } + return 1; + + case KEY_BACKSPACE: + case 127: + case '\b': + if (viewer->grep_search_query_len > 0) { + viewer->grep_search_query_len--; + viewer->grep_search_query[viewer->grep_search_query_len] = '\0'; + update_grep_filter(viewer); + } + return 1; + + default: + // Add printable characters to search query + if (key >= 32 && key <= 126 && viewer->grep_search_query_len < 255) { + viewer->grep_search_query[viewer->grep_search_query_len++] = key; + viewer->grep_search_query[viewer->grep_search_query_len] = '\0'; + update_grep_filter(viewer); + } + return 1; } } @@ -7705,42 +7843,44 @@ int handle_grep_search_input(NCursesDiffViewer *viewer, int key) { * Select item from grep search results */ void select_grep_item(NCursesDiffViewer *viewer) { - if (!viewer || viewer->grep_filtered_count == 0) return; - + if (!viewer || viewer->grep_filtered_count == 0) + return; + // Get the actual item index from scored results - int item_index = viewer->grep_scored_items[viewer->grep_selected_index].item_index; - + int item_index = + viewer->grep_scored_items[viewer->grep_selected_index].item_index; + // Update the main selection based on mode switch (viewer->grep_search_mode) { - case NCURSES_MODE_COMMIT_LIST: - viewer->selected_commit = item_index; - viewer->current_mode = NCURSES_MODE_COMMIT_VIEW; - // Load the selected commit for viewing - if (item_index >= 0 && item_index < viewer->commit_count) { - load_commit_for_viewing(viewer, viewer->commits[item_index].hash); - } - break; - - case NCURSES_MODE_STASH_LIST: - viewer->selected_stash = item_index; - viewer->current_mode = NCURSES_MODE_STASH_VIEW; - // Load the selected stash for viewing - if (item_index >= 0 && item_index < viewer->stash_count) { - load_stash_for_viewing(viewer, item_index); - } - break; - - case NCURSES_MODE_BRANCH_LIST: - viewer->selected_branch = item_index; - viewer->current_mode = NCURSES_MODE_BRANCH_VIEW; - // Load commits for the selected branch - if (item_index >= 0 && item_index < viewer->branch_count) { - load_branch_commits(viewer, viewer->branches[item_index].name); - parse_branch_commits_to_lines(viewer); - } - break; - - default: - break; + case NCURSES_MODE_COMMIT_LIST: + viewer->selected_commit = item_index; + viewer->current_mode = NCURSES_MODE_COMMIT_VIEW; + // Load the selected commit for viewing + if (item_index >= 0 && item_index < viewer->commit_count) { + load_commit_for_viewing(viewer, viewer->commits[item_index].hash); + } + break; + + case NCURSES_MODE_STASH_LIST: + viewer->selected_stash = item_index; + viewer->current_mode = NCURSES_MODE_STASH_VIEW; + // Load the selected stash for viewing + if (item_index >= 0 && item_index < viewer->stash_count) { + load_stash_for_viewing(viewer, item_index); + } + break; + + case NCURSES_MODE_BRANCH_LIST: + viewer->selected_branch = item_index; + viewer->current_mode = NCURSES_MODE_BRANCH_VIEW; + // Load commits for the selected branch + if (item_index >= 0 && item_index < viewer->branch_count) { + load_branch_commits(viewer, viewer->branches[item_index].name); + parse_branch_commits_to_lines(viewer); + } + break; + + default: + break; } } diff --git a/ncurses_diff_viewer.o b/ncurses_diff_viewer.o index e7cf5422044ed3aa7089ad8203264ed8ae240907..90d2537e3737607cabdb0dadb3308fc1fe45e56a 100644 GIT binary patch delta 38361 zcmZ_#V{qWl6F!Q@#>Te2vF&7Idt=-D#2eeTZ6_PswryjRobT_PQ+4ZK_uhHYPj^q( zQ!_O$rfa$_$!)&V8)(Uau= zMrfSR7F7)N^c5p8shm zsq8qQ)SzIg`Z%D>iGTm0wHDxj=D2{Sr3%M@q5~cnYV&S8pWBDWW}VV@c6O`6&vI@i zk{n7u6bpzOU_nXp8bXHh@&ds#5X$_OSR-(;@eB6Tlw2Idq9cq|SuZw+sIIix718aEnJq$q!8KtPPZHny=bT3Z7H$UwWRma`v(N)!bQ5=P67$F5lR;4?NyPw5fglk(ij&Ir)7U=V^V{GQ|Gz!=h_po@m70KzVo}<(MXda_UmI z;#}U}5p8$)7&sRECRDJ3Zls@I>T*%iMgUlWwx^*mT{Hbo{VIzKFukDMHbk-_p*$TS zCHPYu<#9-yN7A_AJk9{@AOVPspQo~U>_M_HE@rc_LQGJfum~nU!}?&Lf?%Uee+CPI z2FW%8EKgbEzdaUzcI%J+Cb#Ua!wXP?Fv1|L!6SG~50=Hz&FPx!Vle*6A9t6;- z$WCrtAq*>JvLjFynWg_G2pXf&nbM&NqT!C7%3wgKnXxqvgo9F%?v#OA5V6l_rG;8( z@F4X0~V97;3ps z_|MAlD}tIm%nPQBHF{JD!@YvI03X=dcY3y<&ak|r~VOydth$x?fHJB*TfAgJR5tA<1DFt5L`yI>` zo@A=1P9T=-nV02`6=bk_J2YDl)6A2!QeZmwnNBG^g`YOq%8W4i#0aS36zrZ@EX0UU zji&8AhY)+=4u*5oCfh7akfSo!usKga$GWsvaatja%@)TW@{N*iTNsz3*8k%+TTEep zM70=+!frB`K;ZcS!+I)33`7f4fhj>{d&YUTYXG-@^$F)nB1`MFQo`C91&W)`FjadY zU68*{MR}yJg$bs~r}?#_R%KD*yOnXM-XJ^06`5vK1j08D1>*0_*oQLbh>`ePEFR~U z6juCy;_i<<2hA1717E<;eW6n8uo`I>xf1KN8XA}AS@z@_OBcCPz&f}$&b~6 z-S^24xr%d-KwS&xqVp> zbMv13T`Dkmt?Lso;G|Bsubm$2RwB?E^n{`^pclzO;IDEYX`l`W){iO>U8y?MTgpH+ zf6l}_t3Dl=Ye?O}H+%=?zn}iHFv}o}6doWBIDfVKi~^xSQ_U-8Yru7dLA}r&C{T#N zb(0^HJ^5ih=$8DzHAQamcdY_XK<&w1S$D6#-Qiusg%8+1?=p`1Dx3jt8euk2`r>uUA+QOU7LZM`I`&<&z$?%jvQ?a0 zzwwVP5Te~MJrW0Nq3B?J|BY4k7eupH;tjZ4L;0l_c%NXGXE1e#abKZbw4MdZ6XM?# z7hQ6@NXs}50M!M;t7OEOzJDhS62zw5AQ?y{ObnDy#;#_cxzre}|BUigFf`;fi6+P^ z{@*eGC5Te6uMiOz%3b)u_8+(6q>S`fuMsy`jUNvn2BxwC zJH%;Fub3kkyBqjY{?J@bAUh-!Iv`)!;AbcSHw`Kvfb|*;C7bdhUyVr6aRH7*Lr&rUnO2d zGU=sKps177%27YREcCQk7gt)3PNSN$Ag@CMeI2{;mnZW*|1V=lnBXdM)x*-H;KD?<`^YQuH~8A%|AiiqBkx( zKks+@^7^qY)nt1)_p)4;#Om)X+8Q+;AiB;Y#w6#)GH0Z^BSiPbc0fP;qUI@%NCd|L zl%jm=+5_rwf3!39(0#^1jIjPzqm3-Wp{WAzcQZBBaO2-J9bJ)tJB{hA3cu_^{XnuK z6f2#xvc^04+LHHFR`NRX?ZBEa5hBwtUmJ;g!O>5?#JySmPYxudX7PPL#5(+G;NZZH zGa8xFU|iSBj0bY(fbs&}q@UO1$Z@%iq4?c2-R;5=SnWo3Na^5QU@lSVDK+C&vU<>Y zr$v5xdiqtk_xgtR=l-PPT7JMUzUvKB5;ADo?Y4|p5LInMtQ8~SB3r2meVPaJE^&jhb`Z%8aJ*F6VNF%p$@N#v)G8=cS>o}jtx(1yyH9S>6oeZ( zP@1e8K=_lN_%%QZriD2Fr`!6-CP6}rMMe5fRClvm|BhXkk7pv?5!3k&L2okK$KQLB z?S-XHc7eQde*_Qb{zi%0)-|~|qj(uxbxr-#Efqs?OI`rlF?EWS)CyD-fUaW2Lu9Q2 zZrc79$+i4UUn`6J8NTHqS`YidoCqtxmcihT+R&oJ5?mL`-yE82jR+tg^W!zQTCWn} z%2ROtaG?DA*FOZ}kLDJMuo8nsH0Gl_Oa#ZdF;>zBb+Uwr7D=mKW^Bt4Ns}I4VvF@d zXM*&EJf_Nlt54e@jZSM8aO<^;I063qA#SsFhB-B5XgKuW&%`XIWiuU0cjvJ-X2Jd? zL9EI`*SA)&HqR66%vryudl%dDsW%cvdQ<)u5PP#Oc0p-|Fh0{}x+eY9$e`X)6iQ%P z;aCD=^AvISY64MqvGXl`TtBBlrlevDIni_cnOPu>X#1DP6I?$L5ZGhYemxEBVz|CZ z`oTqNO(4X8*&F)s2u9+lvVP${9ZiJ8R!byHX@?;H?jrC&@OG_vK6m9I#XXA9I5}=@ zloWFmD3GS>LPDbDz^wb6l3Hw5VR&IMc`3I@$jdFF*s&10H;v89M^|NMg%$GZdHUhYZy;!w~H@mbB| z!4vMKF+xNAO+}*Q{iSZ3+<02k;gb`mxvneny3?nF4It|37a)Ni?Lfpk`4|6#pb^wj zHhNZk$I#{Ysex?N7+6n)(w*T5(bhD`u^Kb6m4rQFp{r8>RJyNzBqJkTEP_Y2RFqK~ zDimf5>wQ&O&aAREb`bEtF(;n7pgkJ?Y+1_;Z>JR2o$DFGKYiJF+^{x@9Y>3YM)eyS zcX4TSM@252+Of6W7SGg~E8*Cyoj%}-q-I80ib?p=L$a-OUF47dakGZbB6K2O#jIvX@rXSgz}SJ~ z+mCW(6^rWr`Slj%b9PuI<~_zt5%|$7XIE{+$2%9e)fEX|N==eDtQiOo0^j5WG|;&_ zlV)Fd+H8YqmW|mrBr-{l4szp;8x$uS!1muOQv+&10eFIxbI6CqzH`Pa#g7;1CRdXz zayg%74%tizk*p*K^5Ml`vLO?2ZN~PA?P&FmOjK+8V?B=mN6y3U^p&NaJ`dK~Z$R(2 zPrQbGfKm=Lk9B|}=H$c!&noYOW=&{HIA`?>;9yci61LDvm9}#)C~XB|KK`&Lu1ST5m$@a8Eb}x?Eex2dJJa_aU~!{9t-n$#|UIz=9wR3OJF+-wT?9Z^>3H-ns+~6J-#*(v<12qU} zlkMsZYGcG2Z1oW8Dk=W4a^xn?g0^Yk_Tw@;)|r6vlZ$hyhwxRSX25*}q*Pl6bM7V+ zQ&atT{N7q2oHB#)Ws6~ywbW@Lt*B#A#=mww+LwcKsKwrc^-@$WCrb<^WYw0Zv^IteaDXvDS~LKL)olXrf$9vO`{ z+6w-lxQh&~VBuUN%;){#%i?|mv8{HGXSWRx#RsA<0zRhCq_a244Sfh{mY|Ba))2X8h%wnG`vr`p;0=a@sY zyDxpE88gwvg{N@=GK*W`IyQgG!DC{)h3v|#=hw#=hY11Nk;j$2p5T_g;M`f6HNyxt zi&}`&m5Lg`#@O2w?LnHfA~~b&zKLSx7gG&Vvj*CDc_3I=fXdq1}^I=7Q=hhX@~Y1A_Nu9mTM}W z9Wd~=WESpCU<)EJ^3p<TFFh;|MCD>mw#6nK)jvb`_UIY z*o$>9h{M^tP-m4s+;UKNF)ye5F`H8U`{{YrU|;UOEOOAJdFmACD$7kVbn45ju*N)$ zJZxgJ%(!IriIbA;E*gLLCk_9dK$kY%$OIvn%B2K*U4Ib==fd6~%z!Ymk`t=xWLnir zJSt`NNoGX1&gBw-l&$UhefFB>EcZ_osls3>;|op>7(JT|P0V_djWI@gWB;>lS|59a z{!=McXtExF1#{{Z^-pxYpmCGq9aO%mpF?pdA(8kJtNdbFPj0;-t^g7WU+~e%H=Hho zQJ011TMF4$Qx?EwsjFv*uDaK9794t87v&Yx$#9ZHeW3wjs>^JDV^=S^cw(MyVJ^OG zw1Xtw-$%dcc|Hk3PUU{cvEyD&h3$g>n8i);0x}C<6s(=6#Bb~;;-?Jr`vRQo`mauS z*6ru71VujDxt(&fiR)^&9^josHr=gXT;lZ(hPX+tbP_nHNsv`iD}7X{;yID7O+62s z*H>dh;JyH+`;x`f6Q@z${K5m{QRkzk5vd=1v-O|S)A2#n&%W3WRn6@RQ}J&p1A-}N zB^uJ?T@N7e6djr7q67fMdDa!_IonLiKqRifg?%mq&3V^T+MZ#%!r3q0_n0-*9!U+0 zLi)JQ_Trl)2I!g*_0^JVeM6Eo<41_Uu$7+*W6FS@&Et4V_`CUX8S<^w7eqeMt@dzX zzzTB@Detdtx=PKVbVlCGE17qP&z8%&jH$`2sn@@kNz_ZJER4{-h;+@6v_}Fpk!`!- zmr*K7Yn5$tiEC$@r-yLXef%UZt`^Nyyi;@M?U$-{PsP<3Cf!l$_REP+m?plB{S8be zC_})Wm(w}%$tE16<+M6S{#r+N7fw}_e`=-bWGl$C1GepSV3i=0xcXZ_Bt;a5`B;l1 zJlxLV84_dzBI=O{hmRnukwe`#n~MQeY! zyGjY;R$$oM|Ao~Lx-Il>r?H=D_Myc%>KjN;?mro8PHB61F7oA*<;aF^`x{=RYNu$< z{<_$6!k12=U%n4z0CH20t82ZA`;V6{Xy%OY2+9CVuqC2=lnK7^62~GaClb#U&46L@ zoK!StjeZ)A$EyRshqohw+Yh_h*+=iB35P%U;LjW-i!kgZ{hk{gGaqU(C*0HF_Y6>0 z^%I&S_P$3!V&In<+|oiX)+;9w}{y^CHT*+16@2t(K+b1*e zN4Mk|;tA`$Ub`WRKDHtavIGryXC%yJrK60|94FFkW+A&I=gA7J+*QEsbeiCT z2N&+)pPLa6c`t}NR~Jaxwk;A+NvxWC_BaRcCJNay;}~~s{PY2Q=s&G&f9(Gn$~U#+ zuftzb&Q-V(J`^@R)YYoERMDhvp;oubzmYC5UQ2=O!+A%xF|qJA_Uo9T2_&zjENIc( zf`s?1b=gje4MV3qcBS6oEZzXd{}^ulhRp-4v2(!%`d&YNNt3!p0iy^BHKrrj-!Sjm z!-D3dmGu)}SWlg8Us$^pYs<2T_lKL6gnG^4Moea8{&FYSYf5K3gq82E9&&n41zuoE zar+O;+pE+QoU(_J;7A6i&SUqF0{fBR>5lKVW;TBbnB8ekdK1HKsi=V4mnItQ-dZ_B zIeL#a>A5>Y$_}dvT&1ReJj#@1J99i&f%38D9Idq~?j}6bA?_v;mt)RYR@|NfkfV{8 z<}3u>FXo_c#82FAUE4skv*rb|7svV%7Tx=#8*2MKpS*X5BWNA|oL(VW>Rof6lRgIy zoP$DXfp!c+sc8xA+%wR9@sVIi9TUN7dWyLC1O(ijU~?L!t=`{}l`(Q0o9^psV-%J7 zU0qf7u9A8qxUPy4MW#wJqIe@r*~Bc8wQ(M3%Zrn-Tof0~uwLT7eDuw2C~*1Z%t`1I zYg4olorJ;-Q?X`@FAjmeszD9&94z!TfW6^rrpWJ@aDl&WAOKOm!#n0&8uZ_w-BnN1 z;5Rx;@_YI`ah7Ps>M-fgai#jb2dAzPF|QP-PrNv3C!-7iF}>06ey7sg(4HyEud{ZudyDTkj7MD;Eh(nAf!H@hJ4=yg-~^%TIjCzeIhH zz`AE?lRhRvxdJj@HR%?vfMKhG=p!8JX!TYIdQ?*(j zD!gvXrpadEDFhPA8HQQX$c~A6lmSxPI4!b%jv(ajxTJRLI4KPJ zTYO&4#^#zi2!w}dtLHy4uO6D!{Ls_gML#{@D2LvBG~-dfaxk?=FtHzpPh#_YIK(m2 zsnS2;fj$EkKa^LzVQ-cBHyX(ypNpcy z21a0J75pEovAx)#ZR;4kgqS|T1iZMI{*upzXn85cs|+-aeOkJ)qm>xT2w0?5=PDz! zW}0AHg>7l}dpCa!k%Al=7#(B0zn^NuF)pt%Q0SD#vJ)|0?8KS4Xt!9)j?v=Mt~VGJ z#P~<$D0&+A`m}x#f+=m{M5j7Mwo<0)9kRNjJvkmTpdU+_GA)IHx8SX%dbAlE{d}HH zj_bP7=0234`|+2opgdno-Kf50!`Sw3_Se64?AS^Fy~b0m&IAR>CX0Jiq4N%pvwI2N z0dT`Ij@YGEL&2$kj*gmqqclJ?9#oZ4msMc**bS>rv3Jnit_8<`E+b(+C|6VCtXIj0 za!fH|*hVC5@FMNl%a`0}O<^RvEz!4g;zg8}kI}4lTNPX!3gC;)1Xwi9`7iMO@C7NT z$5aZ~oDgz>5<{aeV0QXu4I?!qX&|sN02p4#h^?Rt#xUpDUma<3Ec{2^?)g=&#eNb& zu$nUHW*1SMi!PozFgv&=@YPoz9)xHl3}&;;EK@+fQ7kJDe@nsL#Tzp$07=1~-V_wY z?)fL*N zg6?`~HHOH}fBKcc?!1Mn6B_S_8B1>aC*U|uZet>$L2CmyMG!ZjH|9pbtLA=hoUTcpsx20dCskVMSR?M~DQHA<&b%-ARi~%|m zN7Ien+dp>vo-4_OH*K$;AUNybJu*LP4tqm*Emzg)W8m*jOP_6Od;}5XcO_&D1d~a? zMc_KvNFXA^EWv*%&|TgF7*&uwd=?xJ!!FVod=Frnr87h7_(xm{kQblr8T_l`U32eP zCnH{0{J9lW8aSP5n1j5!p|FI*>KD=k2+WF9bqLk*v8_&L)^vl?hhqbneKd#%GXXCw z0^{rgjH?s~ntn!IzERK4SswC>A<|K zc$71Y;~Q+~>L?786KKMdBQ5j((=1Ny$YI_ec>?X4=GAo&^vs!hwOiCcIw`gRxfw*z z==}5sZBKhv%Y*o@U9+43SMrUG#tsE)B` zwIKiy(3=NLJ7Q!05{XJ3*at;%gEPoq!Vk&H_m>4wC@Q+Wu*W>5${WiOi+otexkJvC;~$JB)oHD*x4=bA(pvG~!zN{nh3mGpXOMD7E5ob(xZP;@@RKEYHdX!;)umfb% z{XO8e4l>d-h;6w9vFMTTcRn`FoMEc zJQ^jcz|}}wSA%d>x}M(*Q+v;F#c*a!l8C^qq=^tP>&(S6j{8UH-u3wtE2?35XZdS+ z9JjaYVWjlN)X$tH=lHk#B1mVj?`vI*BeGyiZc+zC824$KWIuB#Qv& z@ag+nhtL)m_eCVCGp7Anm{&jJ)h93G*04=#S$24!5uSj>+k9Tqth~cwnf>eT)AwH3 z{|T3nuzlMRgJHo-Bq(3oN$|9F^7Lqd2wn62AcRZh8`OgTZ%aa(JKnBXm2t61JbCg?Vy%=NaY>_HdFN&6d+eVZj)P*Y23%N+cqx}<+8F8Q^9VS<`JQE z2xuS1){Nxh4KGjyYIdO8kwve~WUt{mH%ExgBYor9MXqxLW*Bd2ni(2%;=M=0MV|1> zmbK<@-*Bb9QRX>Ti{+9(Aa39AUaJ7T*;zgJfm@zsL#M?2{ zZ%_Zuxr^7`eDuxNvj_0)Jk&$R%>GVGwweaQ(d`1FY;;`jt_1MV@qWigc8`*oZk)r@ zDVc7gXFWRBF=Bh!GOD&z(NB`uSfW43LC^mx9vy_1{UC@>7UTW1x+Q|t@|8cKRGmVz zA-Mv3xvVcj<`u9Ny%3Y^E^GnYooiQ-jdY%<&~e}4W^|kE?$_zL=edf_sdN3Z9P_%z zVQros49DbejCnlojdcTByUXOKv0of-g@m1Bzw_0dm;&6aM@D-Hsu|k#*ok=qW`q=SQ-> z|GA~2FG2-Fr0s>($MFqatc?Y&=8=5>9ZoJ}jgb#h+qyOxzhyhsWwNTHyf25asz+t+k-rp0%Xl)R6l_3|`h+JuNIVBB_xQP)r+dFHy3ShLm}l|L5npLMX1MNpDxIqct)i|4+ME z#p=CObT1+BHL>SVDDUcCQx&81qISsyPuF_<c~lO&pJVu-uJTFzzi9FMZ1^&=dPOmo)z?K}W zK__Tti}@~*r?}9MW8qV`=wYnAe16948tIiValJKQ`yR|1**$4zmS~QczotD0VPlyg zbW7LhV}cKodJ9lC!qC4M_GDtsMeHKrVx$8bj#M;&%(_Tp#47B;CvArz?o2x!l*N=h0yteS+l1%H+0Z>T9QX*${1iG1`~IO6X=7 zcu2`hQiv;=&yQas05+1egRJu6Fjg-yWyE5uFyT_M;;T2U5)I0DQa&P-DzNlBz!o1Y zcL_{E%)*W76m+3P_Ei)V z4-yMFSx2spDc}eRcttpFNSYZu1lJL$0AzQfM zI#3~yqzjN<^BGOFp_XEuzQpprtW28u7ucuJVZk)%3$(RuS?Nb1YNvkxlIZwu8<(iT z2O4spNUnMwd^Zci%{>zj*MRgm{4oa^Rn2OXjo+zd3AR*$(~uilXwHcqgO3Lwt6TP$ zpsOw9;CzCh4+(_`tC@wJq;-N%!vwDTLKo!zowU>Ab4nuIx zS#bjylv)>^&y1vxjJg|Zy4;RXc(DPHDyM1!_+;4%XxH~CGftvzGJr}$f6OvArDmo^ zV|6#uXo0(WB*E%3pKoB&MLa8+$r~g7xvN?~CYdz*Xbb2R{=@JpjU4_}@#P^@ z)fa!x((DcS&LE<2Y)|Yj0rx{!ml0T&fmeDiIgOM5jCT<^&p%UI380xYsa@S~4#8?% z@F*tL)y&^sCNEk;o^QBT+(Q?6CoFaHGMg>mr4tEdg+#k);5$>s`#R&vKr4Iu=BxH_ z{mzv4NKQpPGf&d8W7MUs2SSK~O8I(2EUl$yvwXYde2oyLq{y)#yBkRh?+aYVk zmFQOQ+wG>O4#W-XW@KNSFN~A=u9=u7Vi_EMTz)}JGcF0O6ikZ34Hur+bY>CPkc7tV z`=^kMt(g@g2+!Fs>%OEB)@)cI+A}s*2q~&`Gb8_|4aw+iN!~OmJ!^3w8F?pbJMxf5cepKH`iczO_~#tqg)?QxDwjCiOmIGo%32M^2zQZ2rq` zInX}9=Cr9A0+<@0v> zFPSM4sO87lKR}A8g#@W@&$bCb_B2x>YZ5#~?a&tCY@HJrbn1<{R*6;k*ztQYm;4Bv z=+|LrSz*6VlN5%7E)Loe(}BC&^!9ts&cfCW{Kmgm;hHTyxUC}N3JTQnXo5|i>@PJA zLlnKhL7(p9glC}G40T8U+QWdTDO6d()0UT3Q=vXNK+9ov8F$7xdWQp^naJ|(k5S9Z zNy0D0+E2vsH6^lhL{C_R+?ZBM??WTt$Z4HhOol@LM(Tz z{X&2z&+kvMOz%kODkK9;a$scMG^PYSnMjWoX=CbTw{=p8>r7kpPwg>j)RAvOGjbnXqg$jA`5~@w>GQki*L7f!hI z-Lz7tmU)oB`FIk`@P&T&m3&eb1aWzwu#fENum+u!IcEc^ZY5dDI?c*^G_R>*9DIi4 zo=>KG#_=b{WoM*hCW`S%h8OW*ZX5adLX&1MK2RUM0e=xjwF%xSBTjl@+5JVPFB>4^ zHz=$v+CzeHUQ@@=tT|Y^jSuBGFAh^yGl?+px+tO_5et9oTAe!`9?P0+r-dD=tG4ZGv@A-Dgu-tDarfe&GnoZ#7RKcRlK!e**^C=L3JvC9Nz_JNfC3!py76iZJig}2+%PV_~<}u;exoY9UBo_^iDdG%bMQfnSdxnH*Lf#2U(OXGHyLPZVm2$j-Z?T*DjepYd z!FelkQ)=f2+i(nUzvE?d*H@|Dq4VLveCC(`b+@jx>1fjOnMYa$AL8r{X|P_bkyAp+ z{h$UiV8qXtqjqbK48njrQx8F*1!)q$^dfRzzOf{=jgVC#^{`~B;^1|;`iFR;j_dE) zY(ywckLb~&6ZZsZ8G^iH_1>orM)iQ;ieLB?+vl+du%8Ln=B6`FJX`3PGqr%vl5m5Rj1k3sN8GB2L4?3XG-sY}`M=Z^ zB4@%lNLi8|1ms<^>7_Q@jvp3;gZlHT?s0z6q;jzrfH18c?ZUfd(C^iWUGjp)#idTI zJ*yOMNbSQ~nN%=mUO$0X-hQ_bub^stpUYtgSSkh>QbYzbe!Ba@@`+LS;$HV$n@T_c zwi)UhV{G2Sq|j@oUmAknoxBp`fG|658^P11*O!2~){K;!(D%1aJsC_-6Z^$%j22#_ zmUWA%8k4VB$>zo2^9qd)Ip4X)(~tDBV4J#dFi26IPXi8FmgMynkbIBUzr;hVWrnmd zJIubJtEO4I`Q-4ceszfOU*mCv&+Ab3oTE7=0nt+Fu2x-Qq=@{!B(ci&SeA(n%c)mT z*@r^>PMBSTBkuN#?x()tlXv~x%@yz2%Py02$ETjfSQ>=P_q8t#wxC1}+{oP~q3Whd z2>$^!j$|ccs5?e6&1Fk|0~%mbt>l29g@cIdDqWgIWhAdMxkiu=LF~6UaNC2tqorsg zK|g^NC3EkP${rvwYtv~Cfn%)DS)Z(qI&jp@FH>BCB@f^}f_s?N_Nu5(Iz|1sBM z4u*jjyYqXUX`6kwxmdR|lk}MLxJi=c&ALWgt_xDG|jp7&X~brO4V?Zn>d8gVl7%SjiQ5Zdxe|es)oC z3s43Y%zu^gg@0lTD?L(vn-ES}vT9=4bXTH-nKuf@o>UdTkv^h!;kUWI71KWGqVDA} z)ep{vKq{Yjd*b+Yll`7Q!V(Q$QW{g~GV?1fht`Ve5VdhiXaJ~TWzfGpC-NYA7O1l@ zb_*2p9{G$O77Ai$ufpCm25rcp;EPbW!{czCIz8|qsdYOF#gHxZ2lh~!_K1kj;jB=`^UR4;tsk-JNwQfn-P?p|VqL?Vj4rV%{ zmo%!mRuuvm!BGsBtsM8>!N1L|Z!FCntW2%jNvTU&K&w0UZ{=$vLsVPLG&7zcHc9Rh z6|IlmGairQk}d`~l-*9PnsZD8?0$;d5xh!jcYy-6$u& zzjbnX!2#YYgiT|!-B=rU=sF$!ozbc7+Pt6Eue1J-=3z}1=~+~%c68*Yb5-Q&5?q#s zznHJZEeY<*zV2#)|MBBHg)yCHD3KejoH&2hS))D6(nV zj8>;y%GVIY;!<6QBg%-f#nu<0pZF%F<^B!seL51F}5TrZQcs9G=^^?hdb)! zYnNT!MkZ9xV+qn$m6N2n;KD%mYb#rfsSp(gcwz1%cPW&w2VK;}R3Sv8=*m&+hovjW z+q5g$O=o>jnpNX>@Uny5t2FYptsYJYK`sxazbAvZF_XRVcG=@;#gz%u^%z@bo5-N8 z1;)KpvL1GqN6H!ohQg3nalEGASgAxL1P*Xv!5XgZLYDx^Lj3^T(o205@R_CvUB%lH z2ft#?^%q4Tt3Va1a{8VZMHbf_T@I(}R*cm=^} zjzEh!53cF}g!8^ODri#_eFXFuoIs@cn2PC)Xq%vLTTxU0cqj^WJu_r z7#v9=9=T1Ca12RMD*2$g8sAb?fwCCJ^HmeWYdx3jWs+{|5$}c0t})p|`c$G+&G#~* zE`Reb)25?N&Wbra3MxIu+J2d$*XE_<`XPM7is_|j)&JrMIi?VKRbpfPYoK!GQ@-F5 zg5oM0d=bnR^{N={Qn4FGi=FQPe67@uz%qP06Y3NwXmanzh(=HR6S3rQU~3KIzG%=H zruVLeKO!@tnuft>z=QGv3qYq){R4KAURIe#smOCxkqF@ff4$!i;X_ktiZe(8kzGo& zt{-HuocgfhD3!Zzwn16-{xdPrR{?(gwk;%3qGL2l%B}|0zAIg4=(Z;WDErx%Td3-* z*Op4-%i|Tkm~K=;Eft_lft=24x6t@{dkAO#`W+~AA#2p2)O~|G zP$Ijm*gc)7q%seMI8+}6Shr6Ol)_Qv!D1yqkgkN#{>5;Q_?M1WIMlvpw7)@JV&UL6 zWFqh2(487=!fd&O=*;`Yy5LS3(Ii$^ti*CXZ z=?G7PdGPJTTqZu4bbSgFtNLKPnY~Phy6b#=rHZ(g1AgJjn)(d{&_hqfDO6;U$k%qsfz^PCiyN^JaJKyh^uBCg~4*sA~%E@v7~R2*|5-z?MX{6>vpS>-FmYP z@SlL@=U_BD2<&AlPS`4xkmcx#6}S)6YoF*fQ|s<5CZk>xeQ3RIf?2L;Vz4}6d&d~3 zxZl_F49=z^B`sM7vaiEbnD*;B#dkoCS6dn3ja&;7)*SRsUByVh*~H!@fMSF1cSD~ zNnZs=0fzvmGnpyDrp+Fxx3%PGDcbAQnsP@mVRh5AW55?w0hncvq1{Z1C7jdT@@uQvM-O7Qzzp!@g7+!4N^{ zIHusuTq?H-BswkO{)99Mjp3s}Bzyk=*KOR+=gJRHNe(WqS;G^of(CcAYrjncd;W;5 zPi9cge70zfwY4j;CFv&KPBM$%@hUfY<}#SYm4fI%5X-+W9#UsiTG;|4*!ru0aoN)} z;iF1w_n2V|I6pTO)7k>tH5Vr^NxllCvR9 zqRWon_=FK^ia&F?@VTjV?wa;o-r|3OG%E8ky>)wjlup9RufvI^x%(y4`!mAN8ljH+ ziL#E6Q$7oG%vHIaf03)X=nxvo5ZCm=*3xY#+5BkW+y{by zNPjP;$ye2u_qpzuyP%@lTC7}YDAB3vyt?zn#EWY5CDH_w9AYld4@n%IW>R{cbktl9 zC=uzx{?*_jRqe(7uM{mQdQu*F>g-y^@2Lo5)V>|_4dTdUF z_&%3V`Sk&(X;RSdt!3p(mRA+@srP%0U;e0-V83oVR`+*s#>5A#I--V|>5{|2TxICr zi`!fb@awb7K#yyP{E;j* z5ZF8N!|JEDTLR2KUfPHP*@8G~YU>nJ?y}ROswVz+dU%CQB84>Zw#O84t{w8qE;_jH;gb$e2!-_KKzV*3f|O(LOSd_Q1z%zQ92RliR_$KQU-5S6C~*xuW{) zNOBb2lqJ_%TJk-x^CgnM@&(h2?IhuJ+NdTN3u72;Ooh9qNj)@xt%WD}wxA3Ans~kl zWv0+!$Dsu}pKgR+Z=dAkf8RAH)~6({r-BviECaB+M}E9|3giSTWO$s;!HQ5RF?H@p zORuDHd{*#=oS1ZWXwsW&F@FrBMvR_!NMcj}Vm-i&=u~_Sz0_{|0IsbhPgPhVjqM99 z@NG{_9?GITYo(lkHSm<`;si#q9U0_Nkgck5fnVhp#QSnl1FqfLX095 zOgr&B*v`dr`S`-l%Tq{I2eo9%U3-x+wsW&0cy>$2!KhrSi5HklMw!u-XEvlpZ&)$) zVARg}&>B~!biZxP2lbT1h5ZU#5BJ8#@?K-}PCS)-fXHV>84385bpYGX3ei#9h z*b%Dc?!#l$N0nLI~t8uMESVth*AET%lS();xY|g1bXmFmQ&{6wW%XQ3+bL= zdBa13CEgVWm4b?#P$XsD<`1~Y86K*zik(3UA+ne`>|ZfNZbPlc>h29A z>Hn*)JAsF)`yU5xXeW%QhR_HlTO*N_eMv=4(Pk-1QK?3x)kKt~WD2K5lFAlEqG(g8 z2&GMANm(M4Y-OqcJNBe)D=g-}{{VKA+E7?tW)HUj}a%^?P5_vIh|#omd-TnGlzw6FHA1eqvG7Ilslfr<;G@zw~ZH z@vCJ@;i?i%UspF9d#p3Iu$-Lp^t4KEZGx~taIoc`6~D(=l;!t`UddbCYp%@i@wIqX zce{2U!PTh{m$k1sLq;XE4T66MW#{GCY;yJf>NXXu2NyH2h;S$&;5)@YsEdXs}&OCRm33RFAYap92~ zf1R1eIi)-&%fe~vGgoz4o%rY#rQw*b)uSA^vpHFBPu!)YbJTlxSg&kexk~s-rE~0_ zzTDfJjkN~YrG=R@=6HFFwp-Qat6eVgNt{wECsE$^pzM_QYU^N*U910}NkQg)VUx)(!_vCec>4O!4U!ylPZQy!%hSr@9k&t%!*cTp<@L1}Zwi%O*# zB>suJBP+G-=&6{X*@Cm1(z*&Ihfcbs=zU$)EqUg{l7-40d+!(>YAKIb^tQUvz~X$% ziQOf3sdc_4AeO}q>{&kl%W&J9@Kd+Lr4V1BcQ+-I$-`8TA>|gV`>|aNUSEv5`#xAJY zQ42&pWLP5niGS*Il}-oV`e7S2HJMrOIIeW+U4=uBzJm@z@CIyg{&Me+57 zx|-j7{XG5pU%5Mug>^Wp7(M>EHd5Omd!4U|OQpqJ^S?qy$w|t7hdRBYs6VbL%@sPh zsmSP^s@HLug}qbkZ?9<>W|t_$Hiy_wx*hHvTiW*X+k(tXp~;I+Y>03Scz*fm;I8Kb zZ^M(`_IF=(l9K1Y{a#%Zk}^*-@J;jCHB~3tOQT|k&%HjWX{Vm~c*0w!ZO7sbvOC+u z=WF(DC~ZC2dga=7n}9unNh`~@$C*6~YZtP#Ve(F^w@=X)^Vhmy72G?z^;Yllbt-!9 z@n@1(2&}T=CZDlrb4vah`tn)wm-ek?k_Ft!TW2)Rubbr?RZx}7|JXO#Fch{+;>XYvz|cb?4vmvGISAId!XN=sB6=OTRu??|mWU`P0Jz zZxkl2d2F&I&f$<}ymx*PbM2gjr2AieTDHnhN)%=lGw-{_?YUd76YB8r=gZo^-}n>V zlRi6HrJXfN2winu{BXoQW38R$q3oXZFDLeY7-R5U-A+z7eWILZ^{Rxo62p9@wy>46 z3WUCgd}tlgyl3X=x8CVq(&rXU=g_vd?@p>rnA!Vc%S^VyvqGct>w&$Ro!0jas?Pja z7b{SDx3{#U{qdDUt}AP@CbT#eE6+^miR#*v);LZO;?KL~)8cnwQS-%xX-AwRLl=d$ zWXGM4A3wJ$*lyAEd(%p@cb#*s&z=~?pIoc}96_iUBh`!iz3ECKbOV`$U(xFhoRwHLEqrp1jNuWi`DU1xC1 z-{Y^t7;pKadXt*@2_4jXWUDRp?h9Wu&JuCoyLZX%s6zt|xpy?T9bC*ku)a@_=Fxe< zl0Vb5Q#vh1x!qW8;9lJ_!I3JaYQ|8Eba>zMXF_ct1z3B{_BCsMeto#Q#dx{hZ+!!a zoBrP(+EkxETJg00c#&g$MnKJ3t-$9=?`NyeJ!@}LWMf}Dx7>B9j%T}PNnP1oy)8;! zlNJ`YD|KCUYToyDy+ZK;pW4ZPuGakUfBZ{g0e=y~nY`iJi% z{pfG>SdtpHYtd#&UkO&}Uug-}A?ShS`U&|d^yk) zX28B^`Q+>#wd&r0X{Hm;`7MiaFShz9u}3;S*XVfokABhEwEM?jxdtpOWVXv%yN$Xv z<8zv`zSfIH!)@;@gNmFV)VLQq_5JiObQDOJOlhi&;om$cJ63JEl(%PqW{F=+@ZGy% zQUaF@z4VtI5(Q3idJmF>1|R-%4p$_IojtFPMb%CmKj*w;bofJf9-dE z5FosDxqaAGVgAR%);ofo4G&lrn}j@D^-X!Q&Rl8#K zTPRp0cYbVxug(55U#>;?uPeX&vty%HPD0{qQ??_>sEgOR)midg&hTlBs^8G$`jxzC zzI-+5JDI+fKkO$g5{cRQVrQx77?-z~e16?O(G_*~TpLSXF(&VxUPE7IR!M&J*um=W zeYbpmELg8SgQIPDEnr>O&#v0YO@E8LM|D?O7JltqB$;{h$eN0`+B+f*FI}j)d3f3E z^1_>2E`BjsBNOX=u}pqpTac)D!p8#_X3h`$IW~JDzay$+UwvQoCGe!M%=w|~KtzAj zz14~q71Q1>ba=v)_Fr1P|9RX7?VAQS{i>Dq*FAT+!knL4S!Y%;M?BtgomIJp#H;*H z>rdGQQz8ZFVqt5X8n@et%dxg~R^N`Oe&M~hKy^m?lcF4nck1D*$4i-X*PeK1X`dRl zKk<6|nZ=Do{K51jy;SGjOHaR9%hKf(S{_~{71cZA!by$Q+8U034u~h@#HTGkCz-Bh zKiFJ0&*i*x!ppsjqH>RyYF+B-xOQfjhn1829FK@i$8KPKkYw&i0bSJg`DPvuLsU+kEh8h6`6TwYM` zA5`n-UT|A5?n{1GkPUmt`spcEceS&St_fEVRcs$(s?R9$e-T4=?_GYii%6YY})yhFua6;pygNn+= z_9np9RkMv;wQ6rhPzHb7;ld7+-}1cb&Qo)2QNBs3_Hp*3?=KCBV-!qX%J_b9mx9cs=CI7qqkW_f2ERO|l<9KIJ7(P5WgnQiNxPRs zyyCZwPrklZTx?&_f?FVBv88SezvuEoBOB)#dz`~>-v51QmBQ!&+k-2Y4Cf!bq+8(g zsXt{_uwmj~rjC18{zs2}1D5ZsUS8TJ>^r`3CeLSi=RUWcvk%mNzAMV@tu+aWmY=fp z*s-;}H+5E05Y2g`4`_HCqG2d%7 zob<@Y@{6iS>r2-D^G<$8=j#0aT0YU}@}5Ok_neXFs1f~WxW>2X{)LPUQ}th@zWC7_ zQt@@t)+;ipNjkw_J@tQF8~7ZuNH}4U=#Gg$6Z!4bPw8$mzOCE#miPABMV`Igf_EX| z?iryo>pnPjklU;FB}R_oA^xX^?eG- z&RQyq$5!1bd2V{Z^=hA3#O96tU;b*@e)YFmJN#~T}>AA}M2TunQj)MVPKYppC&v%De zdlZ+~uksd*@3N|uZJSbh`1GyjN5x^jx;3UrT`3vPj*(|x+FmL&*3Q?GdUob&bmE>) znRxGRCy&R6nOj9tDod*~6V_L3EO<1ndUib1n0M*whaIbDNB6WezF`ZN*Hv}#$`$rp zmlQ01a;qlQYaow*H)LzAul=+s&&IEO!~Y;&6H?H$>-q!lF&Y*P+m%DNicRQ@JlS=C z`6B45_PE{GWG#nBFKN5%>?x(zc0=HF_Tj;)!EdB~C`z%lh1!PmR-9G6bnxA7izQoQ zxhPaT_q53o?+Y3+%HW2VeFfV{woxlvyW)tyi&nLdcGflD{i{bOkJ{&R`N?~&wo%RR zu5aT$a<4M9%6s{`TE8}{zs{^_{(@OUQXwZNoQggf_Ng}E6Hu{FxTWtAxvg#&xAL!+ z8R*z&7#gxRC#&5n!|u{3{=_xKj072%)RP6zvtqJ$ccjUTFH(r#5~w{PJZIynDxZy= zpA28`Ul;Jf=}qZcPY;dRr~K!A`_f`&eY1z-`krn7XqxJ7mz;waJH7UFZ~3WXQ>tq4 zgT?4^uv@e2z*I%cu0wqvP8I$gzt`2Vs3hs-`1(l)FwfljOsIRR&jKR ze&b{|duWDvaOV6A{+06Cdf_FXa~0e8^0k(GcURBIe-<=dH2?I>seZo?88&O5J#-_o z({G~Y-1lAaiBp&hW}K)xE7)D;DY$)bs)E_qp632n>bd&5lVf8ff4y2#Cb+g_lfGY8 zQGMPHj$81fkm*7VvcBBOv(8=b7@QS1<=4Br)6b-Pj(Nv2$gfo1*!M(fX7;wt{e#}k zFZiqHzS^OE$k2H9(=ET`wkV%o>V0s@zVeiXbA3JE_-?fbUO6V~?WedWd$;_sNk6kJ zw)xwvggo`vlk9KL_1F?Ig=wHQ$$Nva?O5jB#??%nqMU1=Jghn$+`I;EwP!5%+V7~9 zW%lCstoxf@M5*|mPwhTlk$2R|W546h2F*(Ci@jrJ^Y`4fJ&>g*wP0b0QSLs$A?dHW z$7j5j`Vu?0_KecqixP6voL#S78OLs4=ylor^5$*lf_9uts&u;SuxH(lmS3?>Z`W?m z)YV(}p3ArHS$^-(x=7`dmjaDux~arACC5!jt4<4?otJ)5Jndq;^V;sdSB-a_c1|{$ znYZykk7th7+81to<~&i0%A=VIb1DayJ--lmD&?G6`p$Ko&7N&$twKwzHV@zLyXbse zp0`M4!IjV%*YXxdBo8R04umPL>3wHp-)kG%re2>pkbkXhJI_H;rk1^?m-i{9JFTXe zGu!cD<`%<_I=k*FPJiY@+p_l?=Y3pPbt%y-z0JBIByLAeY=$vEy=wEC>>V1(AMwQgqi60>aXBMJ1?@a78o8zDONxAC)6*E! zpw+)qTN@nix}~LT$i1&lO~R{$!(cOtCnv#a#6F>)gHKUb%0K-=(`OPN@!> zu;+E&bJ_id!N0P(Rq1W#ugk?BKC^Fkg0p?(;G4PGN(C>=Z@ik*=VDP-pffW~N9oZw zh1AFC^J^ond z+CiRAZ-CJ;3&-oSUiV_(ynhqY>|(U-&9@a73OX9{!}AAc4l3AJ?%E6<1m>?RnRNJ` zhLUZj!@VWW!wjW?6JkI7hnF_z+%>Ar7<^Y%{@7#9{o6Iw$}Slm%hd^q~fmMhNX zO)pr(i?}^)zS1Szte@-EEG-`3^1BwDkXWI2EwTGp`+n(5PYhg?k~5S(-+LwXu3m*_ zxOxAk$0bJ{3fObETnU*M*}VCZBzx>tZsWA@NBu^RvUyF0$(8X(nbO<+xGj3sMmZL@ zPa1Wp#7m9Uscd;r9+qJu8^7%>>-5oE^Mcsrp)398H{Fdob$eSxf%Kd;`%*=-AME#`_<-tZI3NMCn1yRucY>StP zcHVkZ@cy0u_lq8daXD)kXy35= z9Z^~j4ZX_$I`r>2DPR0lA;xiROl5$2+Z8*W;b9fmQ^V{B#XRcw=YJ(0TLrzRekgx@ zdNU($db9AX(F_+L_$EAsu^YI;#WBEvi_;@3S(oL|kLo8-i%HZ!Y4BgD&wWrx)nYxz z4F9e|nEEHo75#-pJ?Ns?rjv-4mV~iFLc_%R%QMh=v1w>Yx5iW+SI7b)dKixz00G5}%#m6QYJ0=VPl#OCoKdciC|4Vi%WyR6=x@OOPDPJX_;;B5 zTu?L%a3RmeOb@e3997o^0TeTq0_G2D;R&)}@ST&WVPhsgEN8s!oN0?2a8s2}vGOvaM{utCwnpaC*d z1=apva?HAb`I}-(%=EZ>@x{^}m_pt}usA;2y#Wt$5Wz>KP-9^r5pp8I8JM#`1>{14 zH%v#-6@W27aRbT1h`Rs>G6P@r>7L)hU6TZ^kQE6YgRdJ}Kmd6W!Sp7v1%;5EG1IfQ z$pTHPH|T;KKt!wI6RH5P2Pw`WqJ1n;bP?b}eoyddE5zSG0OSE8Ila9nfGo&zcni^8 ztHjY7paHTWW_nua=%pyyk>CJ4Ej(a4L`C}%$)B2{u0;SI=@U^)a6TU63ymd<63?6#azY%b4Gx=tj(R&*?eu2LXSm!`0thEotgaT8l#EK;<(^uQ{k(8{v1kV_Qa+QHZ z7+piKD84iK1}Y#A5xk3yq9;Z(M5eG5X8$`VClfT^Yoa-nx)R!LgmhoDE2YP=(qnYT zm(G}I0i{<6rBB5<(S?*gCNvG76<$YoQQCl&p6cbssNJ7{4Ldzf9d(*+c;`4&mI7Rg z8ESwjOf9GyWC5^x|d z#Y~TzqlX6O23#R~6VdeTZU7KKjwGVvaC9OlgnWaDX5kCU1JDJzoQP)QNv#L$Q51I) z(I)r|A_TaQ$7rHypnJY&9_qO~2!O1Kncg{qQEef8kVP@WZlo6VeBS^o?;<5Le3<34s4V9+ygwwuL+)jpO`*_M9BUB2ag36kf&;)Wb`;{@zJFY z7-J}&hnb#*Tn?IrWq&X5YX3XT0aO%H0|Ag15L^jgxKJPJG6~rMGd&9_ z8fX@FfCk8ih-i5neF8AYQGA|Ydi$jTQ^?l|rnldHz=K>yaPTP9wK|Xpxe+tnwMz=9 zYlEn3qH|Dfdh?dxbC?`pjHjZFFw@an6jAgBz=7;f@UrQMK@?^lCNO2w!Jv zR}#_NC!y$05CHiP5xp0`(i8_-kQH_R9k(=|b2ZQa*@R#^dO2WVBIH7XYe%E5Hh>DqEtu&|q9%^UJq9o&D3+P`@4#g74Vp6G zK%PtRGW_6R23#S#W2TdT$H{|%fMQ1UNE?hRpb(aF(aNVID>?6p&|^&0uR&sc0$wi$ z0wk#<3V=&3SjKv&+jPThDHOe8q!w#4X8I7>i#OL!kOiZI38o(!V?YDsWFk3lB1--V zFqss$5j;ErF$0)FX6pYtqdfcw!T~(UW&~fw59-T6BE^hNBT1;26cw;?1g=aEGkH8} z>I!NqhsfnO1vOm(I8xNfu$y2te3l<3vX(MXR_Xcw_ME=(DgyzPj-^X5-I@g`gltc6 z5PsI$47wou5X{0CjUd3DL~#_s^mD{zz=fPc@GEK5)h8eT@>_x@zzt7*8bKE19)j

CMwgD^c*xm{y;E2NnZg|mf~Lo(~~p~m_nXvgj%LM zN>9=(z=J%WU?!d}y4h`2&U=#qs}%o$TPOL1Mi#UN3KkoNmqO6)Nk12H2VzW_lv%Td-AtIh|q;f^RCI=zYKxatM+9=4-^M zfCo9Bh^8-WB_I*C zLe;sz6|yT4Ek7MG4+tO!5bQGpu>cf8P9!)DKMG`lF367v{v(W{p98iMb#AU7=>p?B z;8G>lA6%L4==O7{@+1%dd4>fl(tJD?mFFW~Oz@ZUh@BC85xh7aaUkN;1V>#!oQyb+ z;ExH2OA*%)EJ#G$g_vQ9igeFwFQKv&sF(oC*-I6~QT}pVX;mBuOhu z@|&o2AJ_Ubl7t}%0$__Qz*MCcY9Na$u?(zGB{&7J;V(GG2sA)mftjAbPiK(3qa?l~ zNvK~fnZlIk(aLm``7RNv7mY$IP^|_O`V&-8p)3(=g!Gn3R6=DHz?ey~I%c{X7Y-vf z102XJiD>$ZBI*@0WN(7$Z~g*-0P<;q>F;VUfkMdH1YZ_GZ9f8CkSht6#ZN0OfIW*k zSGE&Oe@Qh&te1diY3d`k7%l1TfvX8Af&eNVOZh(xBj|e$U62LYf?z3p?R5hUki7|h ziW@x!m}(TKVWww!|54PJGGGe%H4#m>{Ta3WlVG}SNstJmr`w=pbn@Rps5}o;P|UCd zg|n%}YQTVtt*CSWaHtaN91f-D#0NzRT>!3-GylVIKpRw%-vSJ_r);Rv?VB(-VfhjJRDA>BLOw$9AIu3L0P=kz`7#tNR1UHr z*AcuMwnBY6K?7vQQdFEkE#NRm$)WOCz|^2vfnd7lTEG;t5oWsQd@mI33V4tM36|f9 zI1wa5en=!ghfkERKn3Ja1k>N)`~VD1iiPk%>7Li(Tir>316dw3-L-?;QQNw}6|y-I zO~0651q6_tiD^T`>AG!C&!ZG6%4=D1Jh4|1=c+7H}aq5=?ik z2LwPKWs7Rl11sBz$}ErtS&iUs%u7H6WG8|*xuNJIfT>M!G-i5I={cufHbK5cM04Fy z^i#lt{GNz@G#2q!kO=uV!F2NRpaQa-9jZ;YUH|{j)^jLcNN^e6k*RNtAUhFEx4i?n zLJlW*(?rzRc_4tCPVgOkIxYZ(kY5pOGzLXCfG)^C{|}Y{Y!1bW%TaB5i?QudSr2d_ zTVbZhohFHRH3)#bgzik}iZ3ZL5F15?P& z1T*lS+6Q=$$E-kY(LE1PLFLIH5wad;dfc3Kh}VM($cM=2|6Mm2b19A|m_GGT55kb| z5nPCSUISbywrMBW0QQmk4B_>3pLDor{Y1Q^heF>M%Y#DLnkK<|Gf>zf&_yxB76{;C z2Vm<`l?6oQc-*z~)ZZ3ToJ}x2FzR<$6f<6-l+@3zVC6PkneMMUJ~=Qz17yjSs2_B< z=@XG6V9ujhn_wP3PZjc}8iG&ZPV9uv_FA*Jqw`MRfg&ajN{XI<*;6Z*$Bp1VT-V72Uw-elh zZ?wfg1!Osge|NMv8C13a3$f~RV-I>mspt3&bf^3bMo^xe<=3fKY3n<=CMEjYd=nH@g`8p9zca1t& zA-53G5oRb_3}iu;UxSKt+jO)xXn^d1nVz*Ob5wmBU>Z_<;QxpY2d0qY2(A@F(boWv zVw(>{@)Qfi1H}3qOSE2oEn3o@smJS$Km}~gnP4ZpfA9fgA;qBtpV3FzE&>kZ>zL_@ zpuhQf09+xzB$z%X>w$n`n@)o9CK4JZ*3)14h~Wzj-5UM=b{gn{t=SUH##dBok61+U zPJ-#XH-Er|96>PsMQ%I@fP9T$`g&XdvLKfbOka4gV*GZFm~M+bpK$YBJJ#+-sLU4{2iav<=1i@+Ah^b$D z!RTy)J?0>OjQ9;^y65}wJYWBcMN+0 z)0E<1%=EY&)KPQ_Fopb_h&IA!fY*RWu}v$%^o_jOEo=aTTCmm;mFW%Y2{@4V6HM>OVZaq~6z2a83_o3`kF>!mAfnA?qMp0}g;cc7 zM}oz1M+b@Zbfcm!D4cHX1#XRc{x+vNqeF21bX462a3OnQrZ?ePeEHcA0w9MHJWmuw zp95KtGYM{oouEDsKm+7g{|8gQGq9le2WGnG+wgrS^*aN|(i>1Rx@+B2P+1o6Aae+& zcK}n62$@Up6lD~>5mZ19`VYgy5${|=p@3mY@i~HV)71MD4&==L@FXf4j6=!q16Ro9 z1P@6ft^)#!Z8`}ajmQ0mSZ{`(FvT~baC#y%r=hYu=z^`8Vy36@f;?h(z_z0JIKlD? zh|dBp8ljEXCK73c!WjKt$6&i|hsgkVkF$cVP7KDh;wAs}ih-=Wq^apqOC| zcuS}Sb^4|jtYbvwv*IW>0H%-=G1DDoV!l0+g!P<=rvDxNqrmv2l9R*n%)8-z!ma2g6X#@DL?@EHo@2NspC27S~bB% zlTq7WKo=Fw7#QgSV;o>Fqe_!O7F?(RF1283xTC6cx1~{0$Q%T~T3i&fVI+pN6IZ79 z(NY;y+c&RanaHz=jH!94>qW*g7(9^WD5B4p4_X~XjK|-&36l#^zb554QEN{EPbU#$ z>hU%eE*^qbYB51557l|61$3N6^n^bx1KXTMjD$Zlfi$@M)C1b!@^vqm?IL0%(%H)Z ziMJRs;E0RJEa4x$AdOnfm=0Q9sGLWD+6ED0;ZMR~+XiZ_5J;tp<3Ftx8R^Csz;hB& F{C{}7?K=Pf delta 38361 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 a=2_;>W;Iim!E%ZI5x?tC1v6Gi$owA!SlK23 diff --git a/shell b/shell index 1a9a77398528b91801d27264a614f1d3d1f005de..2fd177b33adb775785a19d7be62e0337b4606a73 100755 GIT binary patch delta 15744 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 K;6GNmn*Trk)E=Dx delta 15832 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 A$^ZZW