#include #include #include #include "ui.h" #include "git-intf.h" #include "datastruct.h" typedef struct fhl FHL; struct fhl { FIL *f; HUNK *h; HUNKLINE *l; } ; static int cursed = 0; static FHL top; static FHL cur; static int curline; void ui_abort(void) { if (cursed) { move(LINES-1,0); refresh(); endwin(); printf("\n"); cursed = 0; } } static void ui_save(void) { ui_abort(); /* conceptually different, same implementation */ } static void ui_setup(void) { if (! cursed) { initscr(); noecho(); cbreak(); cursed = 1; } } static void ui_restore(void) { ui_setup(); /* conceptually different, same implemenation */ } static void file_line(int y, FIL *f) { mvprintw(y,0,"%c %.*s", (f->flags & FF_ALLAPPLIED) ? 'A' : ' ', COLS-3, f->escname ); clrtoeol(); } static void hunk_line(int y, HUNK *h) { mvprintw(y,0,"%c | @@ -%d,%d +%d,%d",(h->flags&HF_SELECTED)?'>':' ',h->fl,h->fn,h->tl,h->tn); clrtoeol(); } static void line_line(int y, HUNKLINE *l) { int c; unsigned const char *t; unsigned char q[8]; int qx; int tch; int i; mvprintw(y,0,"%c | | ",(l->h->flags&HF_SELECTED)?'>':' '); switch (l->type) { default: abort(); break; case HLT_COMMON: addch(' '); break; case HLT_FROM: addch('-'); break; case HLT_TO: addch('+'); break; } addch(' '); c = 0; t = (const void *) l->text; qx = 0; while ((c < COLS-8-2) && *t) { tch = -1; if (qx > 0) { tch = q[--qx]; } else if (*t == '\t') { i = 8 - (c & 7); memset(&q[0],' ',i); qx = i; } else if (*t < 32) { q[0] = *t ^ 64; q[1] = '^'; qx = 2; } else if (*t == 127) { q[0] = '?'; q[1] = '^'; qx = 2; } else if ((*t >= 127) && (*t < 160)) { q[0] = '0' + (*t & 7); q[1] = '0' + ((*t >> 3) & 7); q[2] = '0' + ((*t >> 6) & 7); q[3] = '\\'; qx = 4; } else { tch = *t++; } if (tch < 0) { t ++; } else { addch(tch); c ++; } } clrtoeol(); } static void fhl_down(FHL *c) { void down_f(void) { do c->f = c->f->flink; while (c->f && (c->f->flags & FF_ALLAPPLIED)); c->h = 0; } void down_h(void) { do c->h = c->h->fflink; while (c->h && (c->h->flags & HF_APPLIED)); if (! c->h) down_f(); c->l = 0; } void down_l(void) { c->l = c->l->flink; if (! c->l) down_h(); } if (! c->f) abort(); if (c->f->flags & FF_EXPANDED) { if (c->h) { if (c->h->flags & HF_EXPANDED) { if (c->l) { down_l(); } else { c->l = c->h->line_h; } } else { down_h(); } } else { c->h = c->f->diff_h; } } else { down_f(); } } static void fhl_up(FHL *c) { void up_f(void) { do c->f = c->f->blink; while (c->f && (c->f->flags & FF_ALLAPPLIED)); if (c->f && (c->f->flags & FF_EXPANDED)) { c->h = c->f->diff_t; if (c->h->flags & HF_EXPANDED) c->l = c->h->line_t; } } void up_h(void) { do c->h = c->h->fblink; while (c->h && (c->h->flags & HF_APPLIED)); if (c->h && (c->h->flags & HF_EXPANDED)) c->l = c->h->line_t; } void up_l(void) { c->l = c->l->blink; } if (! c->f) abort(); if (c->f->flags & FF_EXPANDED) { if (c->h) { if (c->h->flags & HF_EXPANDED) { if (c->l) { up_l(); } else { up_h(); } } else { up_h(); } } else { up_f(); } } else { up_f(); } } static void fhl_down_level(FHL *c) { if (! c->f) abort(); if ((c->f->flags & FF_EXPANDED) && c->h) { c->h = c->h->fflink; c->l = 0; if (! c->h) c->f = c->f->flink; } else { c->f = c->f->flink; c->h = 0; } } static void fhl_up_level(FHL *c) { if (! c->f) abort(); if ((c->f->flags & FF_EXPANDED) && c->h) { if ((c->h->flags & HF_EXPANDED) && c->l) { c->l = 0; } else { c->h = c->h->fblink; } } else { c->f = c->f->blink; c->h = 0; } } static void redraw_list(void) { int y; FHL c; y = 0; c = top; while (c.f) { if (c.f->flags & FF_EXPANDED) { if (c.h) { if (c.h->flags & HF_EXPANDED) { if (c.l) { if ((c.f == cur.f) && (c.h == cur.h) && (c.l == cur.l)) curline = y; line_line(y,c.l); } else { if ((c.f == cur.f) && (c.h == cur.h) && !cur.l) curline = y; hunk_line(y,c.h); } } else { if ((c.f == cur.f) && (c.h == cur.h)) curline = y; hunk_line(y,c.h); } } else { if ((c.f == cur.f) && !cur.h) curline = y; file_line(y,c.f); } } else { if (c.f == cur.f) curline = y; file_line(y,c.f); } y ++; if (y >= LINES) break; fhl_down(&c); } clrtobot(); } static void recenter(void) { FHL t; FHL t2; int i; t = cur; for (i=(LINES-1)/2;i>0;i--) { t2 = t; fhl_up(&t2); if (! t2.f) break; t = t2; } top = t; } static void gen_applied_file(FIL *f) { f=f; } static void gen_applied_files(void) { FIL *f; for (f=file_h;f;f=f->flink) gen_applied_file(f); } static void cmd_down(void) { FHL ct; ct = cur; fhl_down(&ct); if (ct.f) cur = ct; } static void cmd_up(void) { FHL ct; ct = cur; fhl_up(&ct); if (ct.f) cur = ct; } static void cmd_down_level(void) { FHL ct; ct = cur; fhl_down_level(&ct); if (ct.f) cur = ct; } static void cmd_up_level(void) { FHL ct; ct = cur; fhl_up_level(&ct); if (ct.f) cur = ct; } static void cmd_close(void) { if (cur.f->flags & FF_EXPANDED) { if (cur.h) { if (cur.h->flags & HF_EXPANDED) { if (cur.l) { cur.l = 0; } else { cur.h->flags &= ~HF_EXPANDED; } } else { cur.h = 0; } } else { cur.f->flags &= ~FF_EXPANDED; } } } static void cmd_open(void) { if (cur.f->flags & FF_EXPANDED) { if (cur.h) { if (cur.h->flags & HF_EXPANDED) { if (! cur.l) cur.l = cur.h->line_h; } else { cur.h->flags |= HF_EXPANDED; cur.l = 0; } } else { cur.h = cur.f->diff_h; cur.l = 0; } } else { cur.f->flags |= FF_EXPANDED; cur.h = 0; } } static void cmd_seltoggle(void) { if ((cur.f->flags & FF_EXPANDED) && cur.h) { cur.h->flags ^= HF_SELECTED; } else { beep(); } } static void cmd_commit(void) { FIL *f; HUNK *h; unsigned int fl; FHL t; gen_applied_files(); ui_save(); if (git_commit()) { for (h=hunk_h;h;h=h->aflink) { if (h->flags & HF_SELECTED) h->flags = (h->flags & ~HF_SELECTED) | HF_APPLIED; } for (f=file_h;f;f=f->flink) { fl = HF_APPLIED; for (h=f->diff_h;h;h=h->fflink) fl &= h->flags; if (fl & HF_APPLIED) f->flags |= FF_ALLAPPLIED; } if ( (cur.f->flags & FF_ALLAPPLIED) || ( (cur.f->flags & FF_EXPANDED) && cur.h && (cur.h->flags & HF_APPLIED) ) ) { if (cur.h->flags & HF_EXPANDED) cur.l = cur.h->line_t; t = cur; fhl_down(&t); if (! t.f) { t = cur; fhl_up(&t); if (! t.f) { printf("All hunks applied!\n"); exit(0); } } cur = t; } } ui_restore(); } void ui_main_loop(void) { int c; ui_setup(); top.f = file_h; cur = top; while (1) { curline = -1; redraw_list(); if (curline < 0) { recenter(); continue; } else { move(curline,0); } refresh(); c = getch(); switch (c) { case 'j': cmd_down(); break; case 'k': cmd_up(); break; case 'J': cmd_down_level(); break; case 'K': cmd_up_level(); break; case 'h': cmd_close(); break; case 'l': cmd_open(); break; case 'a': cmd_seltoggle(); break; case 'C': cmd_commit(); break; } } }