"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Mark Jamsek <mark@jamsek.com>
Subject:
Re: [rfc] compound keymaps with prefixed count modifier like vi(1)
To:
gameoftrees@openbsd.org
Date:
Thu, 23 Jun 2022 00:51:35 +1000

Download raw body.

Thread
  • Omar Polo:

    [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
    
  • Omar Polo:

    [rfc] compound keymaps with prefixed count modifier like vi(1)