Files
FERRUM/ncurses_diff_viewer.h

406 lines
11 KiB
C

/**
* ncurses_diff_viewer.h
* NCurses-based interactive diff viewer for git changes
*/
#ifndef NCURSES_DIFF_VIEWER_H
#define NCURSES_DIFF_VIEWER_H
#include "common.h"
#include <ncurses.h>
#define MAX_FILES 100
#define MAX_FILENAME_LEN 256
#define MAX_FULL_FILE_LINES 2000
#define MAX_COMMITS 50
#define MAX_COMMIT_TITLE_LEN 256
#define MAX_AUTHOR_INITIALS 3
#define MAX_STASHES 20
#define MAX_BRANCHES 5
#define MAX_BRANCHNAME_LEN 256
typedef struct {
char stash_info[512];
} NCursesStash;
typedef struct {
char filename[MAX_FILENAME_LEN];
char status; // 'M' = modified, 'A' = added, 'D' = deleted
int marked_for_commit; // 1 if marked for commit, 0 otherwise
int has_staged_changes;
} NCursesChangedFile;
typedef struct {
char name[MAX_BRANCHNAME_LEN];
int status;
int commits_ahead;
int commits_behind;
} NCursesBranches;
typedef struct {
char line[1024];
char type; // '+' = addition, '-' = deletion, ' ' = context, '@' = hunk header
int is_diff_line; // 1 if this is a diff line, 0 if original file line
int hunk_id;
int is_staged;
int line_number_old;
int line_number_new;
int is_context;
} NCursesFileLine;
typedef struct {
char hash[16]; // Short commit hash
char author_initials[MAX_AUTHOR_INITIALS];
char title[MAX_COMMIT_TITLE_LEN];
int is_pushed; // 1 if pushed to remote, 0 if local only
} NCursesCommit;
typedef enum {
NCURSES_MODE_FILE_LIST,
NCURSES_MODE_FILE_VIEW,
NCURSES_MODE_COMMIT_LIST,
NCURSES_MODE_COMMIT_VIEW,
NCURSES_MODE_STASH_LIST,
NCURSES_MODE_STASH_VIEW,
NCURSES_MODE_BRANCH_LIST,
NCURSES_MODE_BRANCH_VIEW
} NCursesViewMode;
typedef enum {
SYNC_STATUS_IDLE,
SYNC_STATUS_SYNCING_APPEARING,
SYNC_STATUS_SYNCING_VISIBLE,
SYNC_STATUS_SYNCING_DISAPPEARING,
SYNC_STATUS_PUSHING_APPEARING,
SYNC_STATUS_PUSHING_VISIBLE,
SYNC_STATUS_PUSHING_DISAPPEARING,
SYNC_STATUS_PULLING_APPEARING,
SYNC_STATUS_PULLING_VISIBLE,
SYNC_STATUS_PULLING_DISAPPEARING,
SYNC_STATUS_SYNCED_APPEARING,
SYNC_STATUS_SYNCED_VISIBLE,
SYNC_STATUS_SYNCED_DISAPPEARING,
SYNC_STATUS_PUSHED_APPEARING,
SYNC_STATUS_PUSHED_VISIBLE,
SYNC_STATUS_PUSHED_DISAPPEARING,
SYNC_STATUS_PULLED_APPEARING,
SYNC_STATUS_PULLED_VISIBLE,
SYNC_STATUS_PULLED_DISAPPEARING
} SyncStatus;
typedef struct {
NCursesChangedFile files[MAX_FILES];
int file_count;
int selected_file;
NCursesFileLine file_lines[MAX_FULL_FILE_LINES];
int file_line_count;
int file_scroll_offset;
int file_cursor_line;
NCursesCommit commits[MAX_COMMITS];
int commit_count;
int selected_commit;
NCursesStash stashes[MAX_STASHES];
NCursesBranches branches[MAX_BRANCHES];
int stash_count;
int branch_count;
int selected_stash;
int selected_branch;
WINDOW *file_list_win;
WINDOW *file_content_win;
WINDOW *commit_list_win;
WINDOW *stash_list_win;
WINDOW *branch_list_win;
WINDOW *status_bar_win;
int terminal_width;
int terminal_height;
int file_panel_width;
int file_panel_height;
int commit_panel_height;
int stash_panel_height;
int branch_panel_height;
int status_bar_height;
NCursesViewMode current_mode;
SyncStatus sync_status;
int spinner_frame;
time_t last_sync_time;
int animation_frame;
int text_char_count;
int pushing_branch_index;
int pulling_branch_index;
SyncStatus branch_push_status;
SyncStatus branch_pull_status;
int branch_animation_frame;
int branch_text_char_count;
int critical_operation_in_progress; // Prevent fetching during critical ops
// Background fetch management
pid_t fetch_pid; // Process ID of background fetch
int fetch_in_progress; // Flag to track if fetch is running
// Branch-specific commits for hover functionality
char branch_commits[MAX_COMMITS][2048]; // Larger buffer for formatted commits
int branch_commit_count;
char current_branch_for_commits[MAX_BRANCHNAME_LEN];
int branch_commits_scroll_offset;
int branch_commits_cursor_line;
int split_view_mode; // 0 = normal view, 1 = split staged/unstaged
int staged_scroll_offset; // Scroll position for staged pane
int active_pane; // 0 = unstaged, 1 = staged
char current_file_path[512]; // Path of currently viewed file
int total_hunks; // Total number of hunks in current file
NCursesFileLine
staged_lines[MAX_FULL_FILE_LINES]; // Separate storage for staged content
int staged_line_count; // Number of lines in staged view
int staged_cursor_line;
} NCursesDiffViewer;
/**
* Initialize the ncurses diff viewer
*/
int init_ncurses_diff_viewer(NCursesDiffViewer *viewer);
/**
* Get list of changed files from git
*/
int get_ncurses_changed_files(NCursesDiffViewer *viewer);
/**
* Load full file content with diff highlighting
*/
int load_full_file_with_diff(NCursesDiffViewer *viewer, const char *filename);
/**
* Render the file list window
*/
void render_file_list_window(NCursesDiffViewer *viewer);
/**
* Render the file content window
*/
void render_file_content_window(NCursesDiffViewer *viewer);
/**
* Handle keyboard input for navigation
*/
int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key);
/**
* Run the ncurses diff viewer
*/
int run_ncurses_diff_viewer(void);
/**
* Clean up ncurses diff viewer resources
*/
void cleanup_ncurses_diff_viewer(NCursesDiffViewer *viewer);
/**
* Create temporary file with current working version
*/
int create_temp_file_with_changes(const char *filename, char *temp_path);
/**
* Create temporary file with git HEAD version
*/
int create_temp_file_git_version(const char *filename, char *temp_path);
/**
* Get commit history
*/
int get_commit_history(NCursesDiffViewer *viewer);
/**
* Toggle file marking for commit
*/
void toggle_file_mark(NCursesDiffViewer *viewer, int file_index);
/**
* Mark all files for commit
*/
void mark_all_files(NCursesDiffViewer *viewer);
/**
* Commit marked files with title and message
*/
int commit_marked_files(NCursesDiffViewer *viewer, const char *commit_title,
const char *commit_message);
/**
* Push specific commit
*/
int push_commit(NCursesDiffViewer *viewer, int commit_index);
/**
* Pull commits from remote
*/
int pull_commits(NCursesDiffViewer *viewer);
/**
* Render the commit list window
*/
void render_commit_list_window(NCursesDiffViewer *viewer);
/**
* Get commit title and message input from user
*/
int get_commit_title_input(char *title, int max_len, char *message,
int max_message_len);
/**
* Draw a box with rounded corners
*/
void draw_rounded_box(WINDOW *win);
/**
* Render the status bar
*/
void render_status_bar(NCursesDiffViewer *viewer);
void render_branch_list_window(NCursesDiffViewer *viewer);
/**
* Update sync status and check for new files
*/
void update_sync_status(NCursesDiffViewer *viewer);
int get_ncurses_git_stashes(NCursesDiffViewer *viewer);
int get_ncurses_git_branches(NCursesDiffViewer *viewer);
typedef enum {
DELETE_LOCAL = 0,
DELETE_REMOTE = 1,
DELETE_BOTH = 2,
DELETE_CANCEL = 3
} DeleteBranchOption;
int get_branch_name_input(char *branch_name, int max_len);
int create_git_branch(const char *branch_name);
int get_rename_branch_input(const char *current_name, char *new_name,
int max_len);
int rename_git_branch(const char *old_name, const char *new_name);
int show_delete_branch_dialog(const char *branch_name);
void show_error_popup(const char *error_message);
int get_git_remotes(char remotes[][256], int max_remotes);
int show_upstream_selection_dialog(const char *branch_name,
char *upstream_result, int max_len);
int get_current_branch_name(char *branch_name, int max_len);
int branch_has_upstream(const char *branch_name);
int delete_git_branch(const char *branch_name, DeleteBranchOption option);
int create_ncurses_git_stash(NCursesDiffViewer *viewer);
int get_stash_name_input(char *stash_name, int max_len);
void render_stash_list_window(NCursesDiffViewer *viewer);
/**
* Load commit details for viewing
*/
int load_commit_for_viewing(NCursesDiffViewer *viewer, const char *commit_hash);
/**
* Load stash details for viewing
*/
int load_stash_for_viewing(NCursesDiffViewer *viewer, int stash_index);
/**
* Load commits for a specific branch for the hover preview
*/
int load_branch_commits(NCursesDiffViewer *viewer, const char *branch_name);
/**
* Parse branch commits into navigable lines for branch view mode
*/
int parse_branch_commits_to_lines(NCursesDiffViewer *viewer);
/**
* Start background fetch process
*/
void start_background_fetch(NCursesDiffViewer *viewer);
/**
* Check if background fetch is complete and update UI accordingly
*/
void check_background_fetch(NCursesDiffViewer *viewer);
/**
* Move cursor up/down while skipping empty lines
*/
void move_cursor_smart(NCursesDiffViewer *viewer, int direction);
/**
* Move cursor up/down in unstaged pane with proper scrolling
*/
void move_cursor_smart_unstaged(NCursesDiffViewer *viewer, int direction);
/**
* Move cursor up/down in staged pane with proper scrolling
*/
void move_cursor_smart_staged(NCursesDiffViewer *viewer, int direction);
/**
* Helper function to wrap a long line into multiple display lines
*/
int wrap_line_to_width(const char *input_line, char wrapped_lines[][1024], int max_lines, int width);
/**
* Render a single line with proper wrapping and coloring
*/
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);
/**
* Calculate how many display lines a wrapped line will take
*/
int calculate_wrapped_line_height(const char *line, int width);
/**
* Load file preview showing the top of the file content
*/
int load_file_preview(NCursesDiffViewer *viewer, const char *filename);
/**
* Update preview based on current mode and selection
*/
void update_preview_for_current_selection(NCursesDiffViewer *viewer);
int get_github_credentials(char *username, int username_len, char *token,
int token_len);
int execute_git_with_auth(const char *base_cmd, const char *username,
const char *token);
int stage_hunk_by_line(NCursesDiffViewer *viewer, int line_index);
int laod_file_with_staging_info(NCursesDiffViewer *viewer,
const char *filename);
void rebuild_staged_view(NCursesDiffViewer *viewer);
int apply_staged_changes(NCursesDiffViewer *viewer);
int reset_staged_changes(NCursesDiffViewer *viewer);
int unstage_line_from_git(NCursesDiffViewer *viewer, int staged_line_index);
int commit_staged_changes_only(NCursesDiffViewer *viewer,
const char *commit_title,
const char *commit_message);
void rebuild_staged_view_from_git(NCursesDiffViewer *viewer);
#endif // NCURSES_DIFF_VIEWER_H