Download raw body.
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
On 22-06-23 12:30am, Mark Jamsek wrote: > On 22-06-22 04:26pm, Stefan Sperling wrote: > > On Thu, Jun 23, 2022 at 12:18:03AM +1000, Mark Jamsek wrote: > > > On 22-06-22 02:51pm, Stefan Sperling wrote: > > > > On Wed, Jun 22, 2022 at 10:42:02PM +1000, Mark Jamsek wrote: > > > > > I'm happy to add an explicit abort key, or do you think documenting the > > > > > timeout and how that cancels a preceding count modifier would suffice? > > > > > If we add an abort key, what should we use if not backspace? > > > > > > > > Yes, just documenting the timeout seems fine. I entirely missed that the > > > > number prompt will time out when I did my initial quick review. > > > > > > > > The timeout should probably be configurable via env vars, but we can > > > > add this later. > > > > > > ok, sounds good! > > > > > > The updated diff adds the timeout to the man page and a couple minor fixes: > > > left-behind typo and ensuring that we scroll all the way up with C-u in > > > blame view if the count modifier is large enough > > > > I am happy with this going patch in. But we should keep refining this for > > improved usability. I think some visual clue that the number is being > > parsed would be nice to have, as suggested in another message of this > > discussion thread. > > > > I just noticed another minor issue, which is when you type something > > like 12345j in the log view, this has the same problem as typing G, > > locking tog into a seemingly endless loop of loading more commits. > > We might want to add a way to abort "loading..." of commits in the log > > view somehow, because in deep repositories this can be very annoying > > if triggered by accident. > > Let me see if I can come up with something to fix that. It shouldn't > take long. This changes the input handling a bit so that C-g and ESCAPE can be used to cancel 9999j or G, for example, in the log view. diff refs/heads/main refs/heads/dev/nkeymap blob - a89f36bf163bbb820b19ad56b23223e7e6078bc1 blob + 4f1f9fdfc9154df7d3212146cce5fed3c6501c41 --- tog/tog.1 +++ tog/tog.1 @@ -55,6 +55,19 @@ Displays references in the repository. .Pp .Nm provides global and command-specific key bindings and options. +Some command-specific key bindings may be prefixed with an integer, which is +denoted by N in the descriptions below, and is used as a modifier to the +operation as indicated. +When the first integer for a count modifier is entered, +.Nm +will wait 500 milliseconds for each successive integer or the compound sequence +to complete. +If this sequence should timeout or does not conclude with a valid key binding, +the command is aborted and any preceding count is reset. +Once a compound command is executed, the operation can be cancelled with the +.Sy ESCAPE +key or +.Sy C-g . The global key bindings are: .Bl -tag -width Ds .It Cm Q @@ -98,30 +111,32 @@ This command is also executed if no explicit command i .Pp The key bindings for .Cm tog log -are as follows: +are as follows (N denotes optional prefixed count modifier): .Bl -tag -width Ds .It Cm Down-arrow, j, >, Full stop, Ctrl-n -Move the selection cursor down. +Move the selection cursor down N lines (default: 1). .It Cm Up-arrow, k, <, Comma, Ctrl-p -Move the selection cursor up. +Move the selection cursor up N lines (default: 1). .It Cm Right-arrow, l -Scroll log message field to the right. +Scroll log message field to the right N increments (default: 1). +.br Log message moves left on the screen. .It Cm Left-arrow, h -Scroll log message field to the left. +Scroll log message field to the left N increments (default: 1). +.br Log message moves right on the screen. .It Cm $ Scroll log message field to the rightmost position. .It Cm 0 Scroll log message field to the leftmost position. .It Cm Page-down, Space, Ctrl+f, f -Move the selection cursor down one page. +Move the selection cursor down N pages (default: 1). .It Cm Page-up, Ctrl+b, b -Move the selection cursor up one page. +Move the selection cursor up N pages (default: 1). .It Cm Ctrl+d, d -Move the selection cursor down one half page. +Move the selection cursor down N half pages (default: 1). .It Cm Ctrl+u, u -Move the selection cursor up one half page. +Move the selection cursor up N half pages (default: 1). .It Cm Home, g Move the cursor to the newest commit. .It Cm End, G @@ -152,12 +167,15 @@ commit ID SHA1 hash. Regular expression syntax is documented in .Xr re_format 7 . .It Cm n -Find the next commit which matches the current search pattern. +Find the Nth next commit which matches the current search pattern (default: 1). +.br Searching continues until either a match is found or the .Cm Backspace key is pressed. .It Cm N -Find the previous commit which matches the current search pattern. +Find the Nth previous commit which matches the current search pattern +(default: 1). +.br Searching continues until either a match is found or the .Cm Backspace key is pressed. @@ -220,62 +238,65 @@ automatically, provided the abbreviation is unique. .Pp The key bindings for .Cm tog diff -are as follows: +are as follows (N denotes optional prefixed count modifier): .Bl -tag -width Ds .It Cm a Toggle treatment of file contents as ASCII text even if binary data was detected. .It Cm Down-arrow, j, Ctrl-n -Scroll down. +Scroll down N lines (default: 1). .It Cm Up-arrow, k, Ctrl-p -Scroll up. +Scroll up N lines (default: 1). .It Cm Right-arrow, l -Scroll view to the right. +Scroll view to the right N increments (default: 1). +.br Diff output moves left on the screen. .It Cm Left-arrow, h -Scroll view to the left. +Scroll view to the left N increments (default: 1). +.br Diff output moves right on the screen. .It Cm $ Scroll view to the rightmost position. .It Cm 0 Scroll view left to the start of the line. .It Cm Page-down, Space, Ctrl+f, f -Scroll down one page. +Scroll down N pages (default: 1). .It Cm Page-up, Ctrl+b, b -Scroll up one page. +Scroll up N pages (default: 1). .It Cm Ctrl+d, d -Scroll down one half page. +Scroll down N half pages (default: 1). .It Cm Ctrl+u, u -Scroll up one half page. +Scroll up N half pages (default: 1). .It Cm Home, g Scroll to the top of the view. .It Cm End, G Scroll to the bottom of the view. .It Cm \&[ -Reduce the amount of diff context lines. +Reduce diff context by N lines (default: 1). .It Cm \&] -Increase the amount of diff context lines. +Increase diff context by N lines (default: 1). .It Cm <, Comma If the .Cm diff view was opened via the .Cm log -view, move to the previous (younger) commit. +view, move to the Nth previous (younger) commit (default: 1). .It Cm >, Full stop If the .Cm diff view was opened via the .Cm log -view, move to the next (older) commit. +view, move to the Nth next (older) commit (default: 1). .It Cm / Prompt for a search pattern and start searching for matching lines. The search pattern is an extended regular expression. Regular expression syntax is documented in .Xr re_format 7 . .It Cm n -Find the next line which matches the current search pattern. +Find the Nth next line which matches the current search pattern (default: 1). .It Cm N -Find the previous line which matches the current search pattern. +Find the Nth previous line which matches the current search pattern +(default: 1). .It Cm w Toggle display of whitespace-only changes. .El @@ -304,30 +325,32 @@ Display line-by-line history of a file at the specifie .Pp The key bindings for .Cm tog blame -are as follows: +are as follows (N denotes optional prefixed count modifier): .Bl -tag -width Ds .It Cm Down-arrow, j, Ctrl-n -Move the selection cursor down. +Move the selection cursor down N pages (default: 1). .It Cm Up-arrow, k, Ctrl-p -Move the selection cursor up. +Move the selection cursor up N pages (default: 1). .It Cm Right-arrow, l -Scroll view to the right. +Scroll view to the right N increments (default: 1). +.br File output moves left on the screen. .It Cm Left-arrow, h -Scroll view to the left. +Scroll view to the left N increments (default: 1). +.br File output moves right on the screen. .It Cm $ Scroll view to the rightmost position. .It Cm 0 Scroll view left to the start of the line. .It Cm Page-down, Space, Ctrl+f, f -Move the selection cursor down one page. +Move the selection cursor down N pages (default: 1). .It Cm Page-up, Ctrl+b, b -Move the selection cursor up one page. +Move the selection cursor up N pages (default: 1). .It Cm Ctrl+d, d -Move the selection cursor down one half page. +Move the selection cursor down N half pages (default: 1). .It Cm Ctrl+u, u -Move the selection cursor up one half page. +Move the selection cursor up N half pages (default: 1). .It Cm Home, g Move the selection cursor to the first line of the file. .It Cm End, G @@ -356,9 +379,10 @@ The search pattern is an extended regular expression. Regular expression syntax is documented in .Xr re_format 7 . .It Cm n -Find the next line which matches the current search pattern. +Find the Nth next line which matches the current search pattern (default: 1). .It Cm N -Find the previous line which matches the current search pattern. +Find the Nth previous line which matches the current search pattern +(default: 1). .El .Pp The options for @@ -397,20 +421,20 @@ Symbolic link entries are also annotated with the targ .Pp The key bindings for .Cm tog tree -are as follows: +are as follows (N denotes optional prefixed count modifier): .Bl -tag -width Ds .It Cm Down-arrow, j, Ctrl-n -Move the selection cursor down. +Move the selection cursor down N lines (default: 1). .It Cm Up-arrow, k, Ctrl-p -Move the selection cursor up. +Move the selection cursor up N lines (default: 1). .It Cm Page-down, Space, Ctrl+f, f -Move the selection cursor down one page. +Move the selection cursor down N pages (default: 1). .It Cm Page-up, Ctrl+b, b -Move the selection cursor up one page. +Move the selection cursor up N pages (default: 1). .It Cm Ctrl+d, d -Move the selection cursor down one half page. +Move the selection cursor down N half pages (default: 1). .It Cm Ctrl+u, u -Move the selection cursor up one half page. +Move the selection cursor up N half pages (default: 1). .It Cm Home, g Move the selection cursor to the first entry. .It Cm End, G @@ -431,7 +455,7 @@ This can then be used to open a new .Cm tree view for arbitrary branches and tags. .It Cm Backspace -Move back to the parent directory. +Move back to the Nth parent directory (default: 1). .It Cm i Show object IDs for all objects displayed in the .Cm tree @@ -443,9 +467,11 @@ against the tree entry's name. Regular expression syntax is documented in .Xr re_format 7 . .It Cm n -Find the next tree entry which matches the current search pattern. +Find the Nth next tree entry which matches the current search pattern +(default: 1). .It Cm N -Find the previous tree entry which matches the current search pattern. +Find the Nth previous tree entry which matches the current search pattern +(default: 1). .El .Pp The options for @@ -471,20 +497,20 @@ Display references in the repository. .Pp The key bindings for .Cm tog ref -are as follows: +are as follows (N denotes optional prefixed count modifier): .Bl -tag -width Ds .It Cm Down-arrow, j, Ctrl-n -Move the selection cursor down. +Move the selection cursor down N lines (default: 1). .It Cm Up-arrow, k, Ctrl-p -Move the selection cursor up. +Move the selection cursor up N lines (default: 1). .It Cm Page-down, Space, Ctrl+f, f -Move the selection cursor down one page. +Move the selection cursor down N pages (default: 1). .It Cm Page-up, Ctrl+b, b -Move the selection cursor up one page. +Move the selection cursor up N pages (default: 1). .It Cm Ctrl+d, d -Move the selection cursor down one half page. +Move the selection cursor down N half pages (default: 1). .It Cm Ctrl+u, u -Move the selection cursor up one half page. +Move the selection cursor up N half pages (default: 1). .It Cm Home, g Move the selection cursor to the first reference. .It Cm End, G @@ -513,9 +539,11 @@ against absolute reference names. Regular expression syntax is documented in .Xr re_format 7 . .It Cm n -Find the next reference which matches the current search pattern. +Find the Nth next reference which matches the current search pattern +(default: 1). .It Cm N -Find the previous reference which matches the current search pattern. +Find the Nth previous reference which matches the current search pattern +(default: 1). .It Cm Ctrl+l Reload the list of references displayed by the .Cm ref blob - cb01bcd4c601aae790a8c7ed5467fd7dc9bc7f1b blob + 3d7f15bd1c716308d8eb1ddcb909da78623bfe7d --- tog/tog.c +++ tog/tog.c @@ -65,6 +65,9 @@ #endif #define CTRL(x) ((x) & 0x1f) +#ifndef KEY_ESCAPE +#define KEY_ESCAPE 27 +#endif #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) @@ -502,6 +505,7 @@ struct tog_view { int nlines, ncols, begin_y, begin_x; int maxx, x; /* max column and current start column */ int lines, cols; /* copies of LINES and COLS */ + int ch, count; /* current keymap and count prefix */ int focussed; /* Only set on one parent or child view at a time. */ int dying; struct tog_view *parent; @@ -667,6 +671,8 @@ view_open(int nlines, int ncols, int begin_y, int begi if (view == NULL) return NULL; + view->ch = 0; + view->count = 0; view->type = type; view->lines = LINES; view->cols = COLS; @@ -861,11 +867,13 @@ view_search_start(struct tog_view *view) mvwaddstr(view->window, view->begin_y + view->nlines - 1, 0, "/"); wclrtoeol(view->window); + nodelay(view->window, FALSE); /* block for search term input */ nocbreak(); echo(); ret = wgetnstr(view->window, pattern, sizeof(pattern)); cbreak(); noecho(); + nodelay(view->window, TRUE); if (ret == ERR) return NULL; @@ -884,6 +892,42 @@ view_search_start(struct tog_view *view) return NULL; } +/* + * Compute view->count from numeric user input. User has five-tenths of a + * second to follow each numeric keypress with another number to form count. + * Return first non-numeric input or ERR and assign total to view->count. + * XXX Should we add support for user-defined timeout? + */ +static int +get_compound_key(struct tog_view *view, int c) +{ + int n = 0; + + view->count = 0; + halfdelay(5); /* block for half a second */ + + do { + /* + * Don't overflow. Max valid request should be the greatest + * between the longest and total lines; cap at 10 million. + */ + if (n >= 9999999) + n = 9999999; + else + n = n * 10 + (c - '0'); + } while (((c = wgetch(view->window))) >= '0' && c <= '9' && c != ERR); + + /* + * Massage value at the input handler if count is invalid. + * XXX Should we validate with a helper function here + * despite maintaining list of key maps in two places? + */ + view->count = n; + + cbreak(); /* disable halfdelay */ + return c; +} + static const struct got_error * view_input(struct tog_view **new, int *done, struct tog_view *view, struct tog_view_list_head *views) @@ -896,8 +940,11 @@ view_input(struct tog_view **new, int *done, struct to /* Clear "no matches" indicator. */ if (view->search_next_done == TOG_SEARCH_NO_MORE || - view->search_next_done == TOG_SEARCH_HAVE_NONE) + view->search_next_done == TOG_SEARCH_HAVE_NONE) { view->search_next_done = TOG_SEARCH_HAVE_MORE; + halfdelay(10); /* show indicator for 1s before clearing */ + view->count = 0; + } if (view->searching && !view->search_next_done) { errcode = pthread_mutex_unlock(&tog_mutex); @@ -913,16 +960,23 @@ view_input(struct tog_view **new, int *done, struct to return NULL; } - nodelay(stdscr, FALSE); /* Allow threads to make progress while we are waiting for input. */ errcode = pthread_mutex_unlock(&tog_mutex); if (errcode) return got_error_set_errno(errcode, "pthread_mutex_unlock"); + /* + * Don't block on getch: the user can either override an existing count + * or if no input is entered the count will continue. + */ ch = wgetch(view->window); + if (ch >= '1' && ch <= '9') + view->ch = ch = get_compound_key(view, ch); + else if (ch == ERR && view->count && --view->count > 0) + ch = view->ch; errcode = pthread_mutex_lock(&tog_mutex); if (errcode) return got_error_set_errno(errcode, "pthread_mutex_lock"); - nodelay(stdscr, TRUE); + nodelay(view->window, TRUE); if (tog_sigwinch_received || tog_sigcont_received) { tog_resizeterm(); @@ -949,6 +1003,7 @@ view_input(struct tog_view **new, int *done, struct to switch (ch) { case '\t': + view->count = 0; if (view->child) { view->focussed = 0; view->child->focussed = 1; @@ -969,6 +1024,7 @@ view_input(struct tog_view **new, int *done, struct to *done = 1; break; case 'F': + view->count = 0; if (view_is_parent_view(view)) { if (view->child == NULL) break; @@ -1000,6 +1056,7 @@ view_input(struct tog_view **new, int *done, struct to case KEY_RESIZE: break; case '/': + view->count = 0; if (view->search_start) view_search_start(view); else @@ -1015,6 +1072,11 @@ view_input(struct tog_view **new, int *done, struct to } else err = view->input(new, view, ch); break; + case CTRL('g'): + case KEY_ESCAPE: + view->count = 0; + view->ch = ERR; + /* FALL THROUGH */ default: err = view->input(new, view, ch); break; @@ -1061,7 +1123,6 @@ view_loop(struct tog_view *view) const struct got_error *err = NULL; struct tog_view_list_head views; struct tog_view *new_view; - int fast_refresh = 10; int done = 0, errcode; errcode = pthread_mutex_lock(&tog_mutex); @@ -1078,10 +1139,6 @@ view_loop(struct tog_view *view) update_panels(); doupdate(); while (!TAILQ_EMPTY(&views) && !done && !tog_fatal_signal_received()) { - /* Refresh fast during initialization, then become slower. */ - if (fast_refresh && fast_refresh-- == 0) - halfdelay(10); /* switch to once per second */ - err = view_input(&new_view, &done, view, &views); if (err) break; @@ -1744,9 +1801,6 @@ draw_commits(struct tog_view *view) } } - if (s->thread_args.commits_needed == 0) - halfdelay(10); /* disable fast refresh */ - if (s->thread_args.commits_needed > 0 || s->thread_args.load_all) { if (asprintf(&ncommits_str, " [%d/%d] %s", entry ? entry->idx + 1 : 0, s->commits.ncommits, @@ -1911,8 +1965,6 @@ trigger_log_thread(struct tog_view *view, int wait) struct tog_log_thread_args *ta = &view->state.log.thread_args; int errcode; - halfdelay(1); /* fast refresh while loading commits */ - while (ta->commits_needed > 0 || ta->load_all) { if (ta->log_complete) break; @@ -2321,6 +2373,9 @@ search_next_log_view(struct tog_view *view) struct tog_log_view_state *s = &view->state.log; struct commit_queue_entry *entry; + if (!s->thread_args.commits_needed && view->search_started) + halfdelay(1); + /* Display progress update in log view. */ show_log_view(view); update_panels(); @@ -2574,7 +2629,7 @@ input_log_view(struct tog_view **new_view, struct tog_ int begin_x = 0, n, nscroll = view->nlines - 1; if (s->thread_args.load_all) { - if (ch == KEY_BACKSPACE) + if (ch == KEY_BACKSPACE || ch == CTRL('g') || ch == KEY_ESCAPE) s->thread_args.load_all = 0; else if (s->thread_args.log_complete) { s->thread_args.load_all = 0; @@ -2595,21 +2650,28 @@ input_log_view(struct tog_view **new_view, struct tog_ break; case '$': view->x = MAX(view->maxx - view->ncols / 2, 0); + view->count = 0; break; case KEY_RIGHT: case 'l': if (view->x + view->ncols / 2 < view->maxx) view->x += 2; /* move two columns right */ + else + view->count = 0; break; case KEY_LEFT: case 'h': view->x -= MIN(view->x, 2); /* move two columns back */ + if (view->x <= 0) + view->count = 0; break; case 'k': case KEY_UP: case '<': case ',': case CTRL('p'): + if (s->selected_entry->idx == 0) + view->count = 0; if (s->first_displayed_entry == NULL) break; if (s->selected > 0) @@ -2623,6 +2685,7 @@ input_log_view(struct tog_view **new_view, struct tog_ s->selected = 0; s->first_displayed_entry = TAILQ_FIRST(&s->commits.head); select_commit(s); + view->count = 0; break; case CTRL('u'): case 'u': @@ -2638,6 +2701,8 @@ input_log_view(struct tog_view **new_view, struct tog_ else log_scroll_up(s, nscroll); select_commit(s); + if (s->selected_entry->idx == 0) + view->count = 0; break; case 'j': case KEY_DOWN: @@ -2655,11 +2720,15 @@ input_log_view(struct tog_view **new_view, struct tog_ break; } select_commit(s); + if (s->thread_args.log_complete && + s->selected_entry->idx == s->commits.ncommits - 1) + view->count = 0; break; case 'G': case KEY_END: { /* We don't know yet how many commits, so we're forced to * traverse them all. */ + view->count = 0; if (!s->thread_args.log_complete) { s->thread_args.load_all = 1; return trigger_log_thread(view, 0); @@ -2688,8 +2757,10 @@ input_log_view(struct tog_view **new_view, struct tog_ case ' ': { struct commit_queue_entry *first; first = s->first_displayed_entry; - if (first == NULL) + if (first == NULL) { + view->count = 0; break; + } err = log_scroll_down(view, nscroll); if (err) break; @@ -2701,6 +2772,9 @@ input_log_view(struct tog_view **new_view, struct tog_ s->selected_entry->idx, nscroll + 1); } select_commit(s); + if (s->thread_args.log_complete && + s->selected_entry->idx == s->commits.ncommits - 1) + view->count = 0; break; } case KEY_RESIZE: @@ -2718,6 +2792,7 @@ input_log_view(struct tog_view **new_view, struct tog_ break; case KEY_ENTER: case '\r': + view->count = 0; if (s->selected_entry == NULL) break; if (view_is_parent_view(view)) @@ -2741,6 +2816,7 @@ input_log_view(struct tog_view **new_view, struct tog_ *new_view = diff_view; break; case 't': + view->count = 0; if (s->selected_entry == NULL) break; if (view_is_parent_view(view)) @@ -2766,6 +2842,7 @@ input_log_view(struct tog_view **new_view, struct tog_ case KEY_BACKSPACE: case CTRL('l'): case 'B': + view->count = 0; if (ch == KEY_BACKSPACE && got_path_is_root_dir(s->in_repo_path)) break; @@ -2827,6 +2904,7 @@ input_log_view(struct tog_view **new_view, struct tog_ s->search_entry = NULL; break; case 'r': + view->count = 0; if (view_is_parent_view(view)) begin_x = view_split_begin_x(view->begin_x); ref_view = view_open(view->nlines, view->ncols, @@ -2852,6 +2930,7 @@ input_log_view(struct tog_view **new_view, struct tog_ *new_view = ref_view; break; default: + view->count = 0; break; } @@ -2902,7 +2981,6 @@ init_curses(void) initscr(); cbreak(); - halfdelay(1); /* Do fast refresh while initial view is loading. */ noecho(); nonl(); intrflush(stdscr, FALSE); @@ -4017,15 +4095,20 @@ input_diff_view(struct tog_view **new_view, struct tog break; case '$': view->x = MAX(view->maxx - view->ncols / 3, 0); + view->count = 0; break; case KEY_RIGHT: case 'l': if (view->x + view->ncols / 3 < view->maxx) view->x += 2; /* move two columns right */ + else + view->count = 0; break; case KEY_LEFT: case 'h': view->x -= MIN(view->x, 2); /* move two columns back */ + if (view->x <= 0) + view->count = 0; break; case 'a': case 'w': @@ -4039,13 +4122,16 @@ input_diff_view(struct tog_view **new_view, struct tog s->matched_line = 0; diff_view_indicate_progress(view); err = create_diff(s); + view->count = 0; break; case 'g': case KEY_HOME: s->first_displayed_line = 1; + view->count = 0; break; case 'G': case KEY_END: + view->count = 0; if (s->eof) break; @@ -4057,6 +4143,8 @@ input_diff_view(struct tog_view **new_view, struct tog case CTRL('p'): if (s->first_displayed_line > 1) s->first_displayed_line--; + else + view->count = 0; break; case CTRL('u'): case 'u': @@ -4065,8 +4153,10 @@ input_diff_view(struct tog_view **new_view, struct tog case KEY_PPAGE: case CTRL('b'): case 'b': - if (s->first_displayed_line == 1) + if (s->first_displayed_line == 1) { + view->count = 0; break; + } i = 0; while (i++ < nscroll && s->first_displayed_line > 1) s->first_displayed_line--; @@ -4076,6 +4166,8 @@ input_diff_view(struct tog_view **new_view, struct tog case CTRL('n'): if (!s->eof) s->first_displayed_line++; + else + view->count = 0; break; case CTRL('d'): case 'd': @@ -4085,8 +4177,10 @@ input_diff_view(struct tog_view **new_view, struct tog case CTRL('f'): case 'f': case ' ': - if (s->eof) + if (s->eof) { + view->count = 0; break; + } i = 0; while (!s->eof && i++ < nscroll) { linelen = getline(&line, &linesize, s->f); @@ -4112,7 +4206,8 @@ input_diff_view(struct tog_view **new_view, struct tog s->first_displayed_line = 1; s->last_displayed_line = view->nlines; } - } + } else + view->count = 0; break; case ']': if (s->diff_context < GOT_DIFF_MAX_CONTEXT) { @@ -4120,15 +4215,19 @@ input_diff_view(struct tog_view **new_view, struct tog s->matched_line = 0; diff_view_indicate_progress(view); err = create_diff(s); - } + } else + view->count = 0; break; case '<': case ',': - if (s->log_view == NULL) + if (s->log_view == NULL) { + view->count = 0; break; + } ls = &s->log_view->state.log; old_selected_entry = ls->selected_entry; + /* view->count handled in input_log_view() */ err = input_log_view(NULL, s->log_view, KEY_UP); if (err) break; @@ -4150,11 +4249,14 @@ input_diff_view(struct tog_view **new_view, struct tog break; case '>': case '.': - if (s->log_view == NULL) + if (s->log_view == NULL) { + view->count = 0; break; + } ls = &s->log_view->state.log; old_selected_entry = ls->selected_entry; + /* view->count handled in input_log_view() */ err = input_log_view(NULL, s->log_view, KEY_DOWN); if (err) break; @@ -4175,6 +4277,7 @@ input_diff_view(struct tog_view **new_view, struct tog err = create_diff(s); break; default: + view->count = 0; break; } @@ -4942,7 +5045,7 @@ show_blame_view(struct tog_view *view) } if (s->blame_complete) - halfdelay(10); /* disable fast refresh */ + cbreak(); /* block */ err = draw_blame(view); @@ -4964,15 +5067,20 @@ input_blame_view(struct tog_view **new_view, struct to break; case '$': view->x = MAX(view->maxx - view->ncols / 3, 0); + view->count = 0; break; case KEY_RIGHT: case 'l': if (view->x + view->ncols / 3 < view->maxx) view->x += 2; /* move two columns right */ + else + view->count = 0; break; case KEY_LEFT: case 'h': view->x -= MIN(view->x, 2); /* move two columns back */ + if (view->x <= 0) + view->count = 0; break; case 'q': s->done = 1; @@ -4981,6 +5089,7 @@ input_blame_view(struct tog_view **new_view, struct to case KEY_HOME: s->selected_line = 1; s->first_displayed_line = 1; + view->count = 0; break; case 'G': case KEY_END: @@ -4992,6 +5101,7 @@ input_blame_view(struct tog_view **new_view, struct to s->first_displayed_line = s->blame.nlines - (view->nlines - 3); } + view->count = 0; break; case 'k': case KEY_UP: @@ -5001,6 +5111,8 @@ input_blame_view(struct tog_view **new_view, struct to else if (s->selected_line == 1 && s->first_displayed_line > 1) s->first_displayed_line--; + else + view->count = 0; break; case CTRL('u'): case 'u': @@ -5011,6 +5123,9 @@ input_blame_view(struct tog_view **new_view, struct to case 'b': if (s->first_displayed_line == 1) { s->selected_line = MAX(1, s->selected_line - nscroll); + while (view->count-- > 1 && s->selected_line > 1) + s->selected_line -= + MIN(s->selected_line, nscroll); break; } if (s->first_displayed_line > nscroll) @@ -5028,10 +5143,14 @@ input_blame_view(struct tog_view **new_view, struct to else if (s->last_displayed_line < s->blame.nlines) s->first_displayed_line++; + else + view->count = 0; break; case 'c': case 'p': { struct got_object_id *id = NULL; + + view->count = 0; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_displayed_line, s->selected_line); if (id == NULL) @@ -5099,6 +5218,8 @@ input_blame_view(struct tog_view **new_view, struct to } case 'C': { struct got_object_qid *first; + + view->count = 0; first = STAILQ_FIRST(&s->blamed_commits); if (!got_object_id_cmp(&first->id, s->commit_id)) break; @@ -5121,6 +5242,8 @@ input_blame_view(struct tog_view **new_view, struct to struct got_object_id *id = NULL; struct got_object_qid *pid; struct got_commit_object *commit = NULL; + + view->count = 0; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_displayed_line, s->selected_line); if (id == NULL) @@ -5172,6 +5295,7 @@ input_blame_view(struct tog_view **new_view, struct to if (s->last_displayed_line >= s->blame.nlines && s->selected_line >= MIN(s->blame.nlines, view->nlines - 2)) { + view->count = 0; break; } if (s->last_displayed_line >= s->blame.nlines && @@ -5193,6 +5317,7 @@ input_blame_view(struct tog_view **new_view, struct to } break; default: + view->count = 0; break; } return thread_err ? thread_err : err; @@ -5885,8 +6010,10 @@ input_tree_view(struct tog_view **new_view, struct tog switch (ch) { case 'i': s->show_ids = !s->show_ids; + view->count = 0; break; case 'l': + view->count = 0; if (!s->selected_entry) break; if (view_is_parent_view(view)) @@ -5906,6 +6033,7 @@ input_tree_view(struct tog_view **new_view, struct tog *new_view = log_view; break; case 'r': + view->count = 0; if (view_is_parent_view(view)) begin_x = view_split_begin_x(view->begin_x); ref_view = view_open(view->nlines, view->ncols, @@ -5933,6 +6061,7 @@ input_tree_view(struct tog_view **new_view, struct tog case 'g': case KEY_HOME: s->selected = 0; + view->count = 0; if (s->tree == s->root) s->first_displayed_entry = got_object_tree_get_first_entry(s->tree); @@ -5942,6 +6071,7 @@ input_tree_view(struct tog_view **new_view, struct tog case 'G': case KEY_END: s->selected = 0; + view->count = 0; te = got_object_tree_get_last_entry(s->tree); for (n = 0; n < view->nlines - 3; n++) { if (te == NULL) { @@ -5965,6 +6095,10 @@ input_tree_view(struct tog_view **new_view, struct tog break; } tree_scroll_up(s, 1); + if (s->selected_entry == NULL || + (s->tree == s->root && s->selected_entry == + got_object_tree_get_first_entry(s->tree))) + view->count = 0; break; case CTRL('u'): case 'u': @@ -5982,6 +6116,10 @@ input_tree_view(struct tog_view **new_view, struct tog s->selected -= MIN(s->selected, nscroll); } tree_scroll_up(s, MAX(0, nscroll)); + if (s->selected_entry == NULL || + (s->tree == s->root && s->selected_entry == + got_object_tree_get_first_entry(s->tree))) + view->count = 0; break; case 'j': case KEY_DOWN: @@ -5991,9 +6129,11 @@ input_tree_view(struct tog_view **new_view, struct tog break; } if (got_tree_entry_get_next(s->tree, s->last_displayed_entry) - == NULL) + == NULL) { /* can't scroll any further */ + view->count = 0; break; + } tree_scroll_down(s, 1); break; case CTRL('d'): @@ -6010,6 +6150,8 @@ input_tree_view(struct tog_view **new_view, struct tog if (s->selected < s->ndisplayed - 1) s->selected += MIN(nscroll, s->ndisplayed - s->selected - 1); + else + view->count = 0; break; } tree_scroll_down(s, nscroll); @@ -6020,8 +6162,10 @@ input_tree_view(struct tog_view **new_view, struct tog if (s->selected_entry == NULL || ch == KEY_BACKSPACE) { struct tog_parent_tree *parent; /* user selected '..' */ - if (s->tree == s->root) + if (s->tree == s->root) { + view->count = 0; break; + } parent = TAILQ_FIRST(&s->parents); TAILQ_REMOVE(&s->parents, parent, entry); @@ -6036,6 +6180,7 @@ input_tree_view(struct tog_view **new_view, struct tog } else if (S_ISDIR(got_tree_entry_get_mode( s->selected_entry))) { struct got_tree_object *subtree; + view->count = 0; err = got_object_open_as_tree(&subtree, s->repo, got_tree_entry_get_id(s->selected_entry)); if (err) @@ -6056,6 +6201,7 @@ input_tree_view(struct tog_view **new_view, struct tog s->commit_id, s->repo); if (err) break; + view->count = 0; view->focussed = 0; blame_view->focussed = 1; if (view_is_parent_view(view)) { @@ -6073,8 +6219,10 @@ input_tree_view(struct tog_view **new_view, struct tog case KEY_RESIZE: if (view->nlines >= 4 && s->selected >= view->nlines - 3) s->selected = view->nlines - 4; + view->count = 0; break; default: + view->count = 0; break; } @@ -6757,12 +6905,15 @@ input_ref_view(struct tog_view **new_view, struct tog_ switch (ch) { case 'i': s->show_ids = !s->show_ids; + view->count = 0; break; case 'm': s->show_date = !s->show_date; + view->count = 0; break; case 'o': s->sort_by_date = !s->sort_by_date; + view->count = 0; err = got_reflist_sort(&tog_refs, s->sort_by_date ? got_ref_cmp_by_commit_timestamp_descending : tog_ref_cmp_by_name, s->repo); @@ -6778,6 +6929,7 @@ input_ref_view(struct tog_view **new_view, struct tog_ break; case KEY_ENTER: case '\r': + view->count = 0; if (!s->selected_entry) break; if (view_is_parent_view(view)) @@ -6798,6 +6950,7 @@ input_ref_view(struct tog_view **new_view, struct tog_ *new_view = log_view; break; case 't': + view->count = 0; if (!s->selected_entry) break; if (view_is_parent_view(view)) @@ -6822,11 +6975,13 @@ input_ref_view(struct tog_view **new_view, struct tog_ case 'g': case KEY_HOME: s->selected = 0; + view->count = 0; s->first_displayed_entry = TAILQ_FIRST(&s->refs); break; case 'G': case KEY_END: s->selected = 0; + view->count = 0; re = TAILQ_LAST(&s->refs, tog_reflist_head); for (n = 0; n < view->nlines - 1; n++) { if (re == NULL) @@ -6845,6 +7000,8 @@ input_ref_view(struct tog_view **new_view, struct tog_ break; } ref_scroll_up(s, 1); + if (s->selected_entry == TAILQ_FIRST(&s->refs)) + view->count = 0; break; case CTRL('u'): case 'u': @@ -6856,6 +7013,8 @@ input_ref_view(struct tog_view **new_view, struct tog_ if (s->first_displayed_entry == TAILQ_FIRST(&s->refs)) s->selected -= MIN(nscroll, s->selected); ref_scroll_up(s, MAX(0, nscroll)); + if (s->selected_entry == TAILQ_FIRST(&s->refs)) + view->count = 0; break; case 'j': case KEY_DOWN: @@ -6864,9 +7023,11 @@ input_ref_view(struct tog_view **new_view, struct tog_ s->selected++; break; } - if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL) + if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL) { /* can't scroll any further */ + view->count = 0; break; + } ref_scroll_down(s, 1); break; case CTRL('d'): @@ -6882,11 +7043,15 @@ input_ref_view(struct tog_view **new_view, struct tog_ if (s->selected < s->ndisplayed - 1) s->selected += MIN(nscroll, s->ndisplayed - s->selected - 1); + if (view->count > 1 && s->selected < s->ndisplayed - 1) + s->selected += s->ndisplayed - s->selected - 1; + view->count = 0; break; } ref_scroll_down(s, nscroll); break; case CTRL('l'): + view->count = 0; tog_free_refs(); err = tog_load_refs(s->repo, s->sort_by_date); if (err) @@ -6899,6 +7064,7 @@ input_ref_view(struct tog_view **new_view, struct tog_ s->selected = view->nlines - 2; break; default: + view->count = 0; break; } -- Mark Jamsek <fnc.bsdbox.org> GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)
[rfc] compound keymaps with prefixed count modifier like vi(1)