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

From:
Mark Jamsek <mark@jamsek.com>
Subject:
Re: tog: nG key map like vi(1) and less(1)
To:
Christian Weisgerber <naddy@mips.inka.de>
Cc:
gameoftrees@openbsd.org
Date:
Mon, 25 Jul 2022 03:03:44 +1000

Download raw body.

Thread
On 22-07-24 03:08pm, Christian Weisgerber wrote:
> Mark Jamsek:
> 
> > We use A_STANDOUT (or A_REVERSE in the previous diff as I've had issues
> > with A_STANDOUT in tmux in the past, otherwise they both appear to do
> > the same thing?), but because the text is coloured, we get the coloured
> > highlight. Like we currently do with highlighted search terms in diff
> > views.
> 
> Yes, and I think that's fine.
> My suggestion was to replace A_BOLD|A_REVERSE with a simple A_STANDOUT.

Sorry, naddy! My mistake--I thought you were referring to the coloured
highlight. At first, I found it a bit jarring so I tried it without the
colour, but I liked that less. And then the coloured highlight grew on
me :)

Rebased and revised diff drops the A_BOLD so it's just A_STANDOUT now.

> > diff refs/heads/main refs/heads/dev/gline
> > commit - c3821befbed0b67d1b48a5cfa3aaa2e022c58430
> > commit + 6aba0b5044ca3682899309aea1d763a451120360
> 
> Works fine in my testing.

Thank you!

diff refs/heads/main refs/heads/dev/gline
commit - 10a16316a03005a07c45b2bbf1b5644b64e846fb
commit + 0822ba705f5de920d06a583df07678d70be35107
blob - c09dbe11bb6e0224ed0138a35da047947c82f41d
blob + ff238905b35da47efd5533334d11290e37f51345
--- tog/tog.1
+++ tog/tog.1
@@ -93,6 +93,10 @@ N increments (default: 1).
 .It Cm +
 When in a split-screen view, increase the size of the focussed split
 N increments (default: 1).
+.It Cm G
+Go to line N in the view (default: last line).
+.It Cm g
+Go to line N in the view (default: first line).
 .El
 .Pp
 Global options must precede the command name, and are as follows:
blob - aaca1ae9bd59788b771d3797213ce44b033f50a8
blob + c8ba3dd30932400e33436e51fce150c7b8c63546
--- tog/tog.c
+++ tog/tog.c
@@ -518,6 +518,7 @@ struct tog_view {
 	int maxx, x; /* max column and current start column */
 	int lines, cols; /* copies of LINES and COLS */
 	int nscrolled, offset; /* lines scrolled and hsplit line offset */
+	int gline, hiline; /* navigate to and highlight this nG line */
 	int ch, count; /* current keymap and count prefix */
 	int resized; /* set when in a resize event */
 	int focussed; /* Only set on one parent or child view at a time. */
@@ -1279,6 +1280,7 @@ get_compound_key(struct tog_view *view, int c)

 	view->count = 0;
 	cbreak();  /* block for input */
+	nodelay(view->window, FALSE);
 	wmove(v->window, v->nlines - 1, 0);
 	wclrtoeol(v->window);
 	waddch(v->window, ':');
@@ -1300,6 +1302,12 @@ get_compound_key(struct tog_view *view, int c)
 			n = n * 10 + (c - '0');
 	} while (((c = wgetch(view->window))) >= '0' && c <= '9' && c != ERR);

+	if (c == 'G' || c == 'g') {	/* nG key map */
+		view->gline = view->hiline = n;
+		n = 0;
+		c = 0;
+	}
+
 	/* Massage excessive or inapplicable values at the input handler. */
 	view->count = n;

@@ -1337,7 +1345,6 @@ view_input(struct tog_view **new, int *done, struct to
 		return NULL;
 	}

-	nodelay(view->window, FALSE);
 	/* Allow threads to make progress while we are waiting for input. */
 	errcode = pthread_mutex_unlock(&tog_mutex);
 	if (errcode)
@@ -1356,10 +1363,12 @@ view_input(struct tog_view **new, int *done, struct to
 		if (ch >= '1' && ch  <= '9')
 			view->ch = ch = get_compound_key(view, ch);
 	}
+	if (view->hiline && ch != ERR && ch != 0)
+		view->hiline = 0;  /* key pressed, clear line highlight */
+	nodelay(view->window, TRUE);
 	errcode = pthread_mutex_lock(&tog_mutex);
 	if (errcode)
 		return got_error_set_errno(errcode, "pthread_mutex_lock");
-	nodelay(view->window, TRUE);

 	if (tog_sigwinch_received || tog_sigcont_received) {
 		tog_resizeterm();
@@ -2494,7 +2503,8 @@ log_scroll_down(struct tog_view *view, int maxscroll)
 		/*
 		 * Ask the log thread for required amount of commits.
 		 */
-		s->thread_args.commits_needed += maxscroll;
+		s->thread_args.commits_needed +=
+		    ncommits_needed - s->commits.ncommits;
 		err = trigger_log_thread(view, 1);
 		if (err)
 			return err;
@@ -3247,6 +3257,42 @@ view_init_hsplit(struct tog_view *view, int y)
 }

 static const struct got_error *
+log_goto_line(struct tog_view *view, int nlines)
+{
+	const struct got_error		*err = NULL;
+	struct tog_log_view_state	*s = &view->state.log;
+	int				 g, idx = s->selected_entry->idx;
+
+	g = view->gline;
+	view->gline = 0;
+
+	if (g >= s->first_displayed_entry->idx + 1 &&
+	    g <= s->last_displayed_entry->idx + 1 &&
+	    g - s->first_displayed_entry->idx - 1 < nlines) {
+		s->selected = g - s->first_displayed_entry->idx - 1;
+		select_commit(s);
+		return NULL;
+	}
+
+	if (idx + 1 < g) {
+		err = log_move_cursor_down(view, g - idx - 1);
+		if (!err && g > s->selected_entry->idx + 1)
+			err = log_move_cursor_down(view,
+			    g - s->first_displayed_entry->idx - 1);
+		if (err)
+			return err;
+	} else if (idx + 1 > g)
+		log_move_cursor_up(view, idx - g + 1, 0);
+
+	if (g < nlines && s->first_displayed_entry->idx == 0)
+		s->selected = g - 1;
+
+	select_commit(s);
+	return NULL;
+
+}
+
+static const struct got_error *
 input_log_view(struct tog_view **new_view, struct tog_view *view, int ch)
 {
 	const struct got_error *err = NULL;
@@ -3268,6 +3314,9 @@ input_log_view(struct tog_view **new_view, struct tog_
 	if (view_is_hsplit_top(view))
 		--eos;  /* border */

+	if (view->gline)
+		return log_goto_line(view, eos);
+
 	switch (ch) {
 	case 'q':
 		s->quit = 1;
@@ -3835,13 +3884,55 @@ done:
 	return err;
 }

+static int
+gotoline(struct tog_view *view, int *lineno, int *nprinted)
+{
+	FILE	*f = NULL;
+	int	*eof, *first, *selected;
+
+	if (view->type == TOG_VIEW_DIFF) {
+		struct tog_diff_view_state *s = &view->state.diff;
+
+		first = &s->first_displayed_line;
+		selected = first;
+		eof = &s->eof;
+		f = s->f;
+	} else if (view->type == TOG_VIEW_BLAME) {
+		struct tog_blame_view_state *s = &view->state.blame;
+
+		first = &s->first_displayed_line;
+		selected = &s->selected_line;
+		eof = &s->eof;
+		f = s->blame.f;
+	} else
+		return 0;
+
+	/* Center gline in the middle of the page like vi(1). */
+	if (*lineno < view->gline - (view->nlines - 3) / 2)
+		return 0;
+	if (*first != 1 && (*lineno > view->gline - (view->nlines - 3) / 2)) {
+		rewind(f);
+		*eof = 0;
+		*first = 1;
+		*lineno = 0;
+		*nprinted = 0;
+		return 0;
+	}
+
+	*selected = view->gline <= (view->nlines - 3) / 2 ?
+		view->gline : (view->nlines - 3) / 2 + 1;
+	view->gline = 0;
+
+	return 1;
+}
+
 static const struct got_error *
 draw_file(struct tog_view *view, const char *header)
 {
 	struct tog_diff_view_state *s = &view->state.diff;
 	regmatch_t *regmatch = &view->regmatch;
 	const struct got_error *err;
-	int nprinted = 0;
+	int lineno, nprinted = 0;
 	char *line;
 	size_t linesize = 0;
 	ssize_t linelen;
@@ -3851,17 +3942,24 @@ draw_file(struct tog_view *view, const char *header)
 	int max_lines = view->nlines;
 	int nlines = s->nlines;
 	off_t line_offset;
+	attr_t attr;

+	lineno = s->first_displayed_line - 1;
 	line_offset = s->line_offsets[s->first_displayed_line - 1];
 	if (fseeko(s->f, line_offset, SEEK_SET) == -1)
 		return got_error_from_errno("fseek");

 	werase(view->window);

+	if (view->gline > s->nlines - 1)
+		view->gline = s->nlines - 1;
+
 	if (header) {
-		if (asprintf(&line, "[%d/%d] %s",
-		    s->first_displayed_line - 1 + s->selected_line, nlines,
-		    header) == -1)
+		int ln = view->gline ? view->gline <= (view->nlines - 3) / 2 ?
+		    1 : view->gline - (view->nlines - 3) / 2 :
+		    lineno + s->selected_line;
+
+		if (asprintf(&line, "[%d/%d] %s", ln, nlines, header) == -1)
 			return got_error_from_errno("asprintf");
 		err = format_line(&wline, &width, NULL, line, 0, view->ncols,
 		    0, 0);
@@ -3898,6 +3996,14 @@ draw_file(struct tog_view *view, const char *header)
 			return got_ferror(s->f, GOT_ERR_IO);
 		}

+		attr = 0;
+		if (++lineno < s->first_displayed_line)
+			continue;
+		if (view->gline && !gotoline(view, &lineno, &nprinted))
+			continue;
+		if (lineno == view->hiline)
+			attr = A_STANDOUT;
+
 		/* Set view->maxx based on full line length. */
 		err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 0,
 		    view->x ? 1 : 0);
@@ -3911,8 +4017,9 @@ draw_file(struct tog_view *view, const char *header)

 		tc = match_color(&s->colors, line);
 		if (tc)
-			wattr_on(view->window,
-			    COLOR_PAIR(tc->colorpair), NULL);
+			attr |= COLOR_PAIR(tc->colorpair);
+		if (attr)
+			wattron(view->window, attr);
 		if (s->first_displayed_line + nprinted == s->matched_line &&
 		    regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) {
 			err = add_matched_line(&width, line, view->ncols, 0,
@@ -3933,12 +4040,18 @@ draw_file(struct tog_view *view, const char *header)
 			free(wline);
 			wline = NULL;
 		}
-		if (tc)
-			wattr_off(view->window,
-			    COLOR_PAIR(tc->colorpair), NULL);
-		if (width <= view->ncols - 1)
-			waddch(view->window, '\n');
-		nprinted++;
+		if (lineno == view->hiline) {
+			/* highlight full gline length */
+			while (width++ < view->ncols)
+				waddch(view->window, ' ');
+		} else {
+			if (width <= view->ncols - 1)
+				waddch(view->window, '\n');
+		}
+		if (attr)
+			wattroff(view->window, attr);
+		if (++nprinted == 1)
+			s->first_displayed_line = lineno;
 	}
 	free(line);
 	if (nprinted >= 1)
@@ -5055,7 +5168,10 @@ draw_blame(struct tog_view *view)
 	if (width < view->ncols - 1)
 		waddch(view->window, '\n');

-	if (asprintf(&line, "[%d/%d] %s%s",
+	if (view->gline > blame->nlines)
+		view->gline = blame->nlines;
+
+	if (asprintf(&line, "[%d/%d] %s%s", view->gline ? view->gline :
 	    s->first_displayed_line - 1 + s->selected_line, blame->nlines,
 	    s->blame_complete ? "" : "annotating... ", s->path) == -1) {
 		free(id_str);
@@ -5087,6 +5203,8 @@ draw_blame(struct tog_view *view)
 		}
 		if (++lineno < s->first_displayed_line)
 			continue;
+		if (view->gline && !gotoline(view, &lineno, &nprinted))
+			continue;

 		/* Set view->maxx based on full line length. */
 		err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 9, 1);
@@ -6171,7 +6289,7 @@ draw_tree_entries(struct tog_view *view, const char *p
 	struct got_tree_entry *te;
 	wchar_t *wline;
 	struct tog_color *tc;
-	int width, n, i, nentries;
+	int width, n, nentries, i = 1;
 	int limit = view->nlines;

 	s->ndisplayed = 0;
@@ -6201,6 +6319,15 @@ draw_tree_entries(struct tog_view *view, const char *p
 		wstandend(view->window);
 	free(wline);
 	wline = NULL;
+
+	if (s->selected_entry) {
+		i = got_tree_entry_get_index(s->selected_entry);
+		i += s->tree == s->root ? 1 : 2;  /* account for ".." entry */
+	}
+	nentries = got_object_tree_get_nentries(s->tree);
+	wprintw(view->window, " [%d/%d]", i,
+	    nentries + (s->tree == s->root ? 0 : 1));  /* ".." in !root tree */
+
 	if (width < view->ncols - 1)
 		waddch(view->window, '\n');
 	if (--limit <= 0)
@@ -6239,7 +6366,6 @@ draw_tree_entries(struct tog_view *view, const char *p
 		te = s->first_displayed_entry;
 	}

-	nentries = got_object_tree_get_nentries(s->tree);
 	for (i = got_tree_entry_get_index(te); i < nentries; i++) {
 		char *line = NULL, *id_str = NULL, *link_target = NULL;
 		const char *modestr = "";
@@ -6359,8 +6485,10 @@ tree_scroll_down(struct tog_view *view, int maxscroll)

 	last = s->last_displayed_entry;
 	while (next && n++ < maxscroll) {
-		if (last)
+		if (last) {
+			s->last_displayed_entry = last;
 			last = got_tree_entry_get_next(s->tree, last);
+		}
 		if (last || (view->mode == TOG_VIEW_SPLIT_HRZN && next)) {
 			s->first_displayed_entry = next;
 			next = got_tree_entry_get_next(s->tree, next);
@@ -6701,6 +6829,67 @@ show_tree_view(struct tog_view *view)
 }

 static const struct got_error *
+tree_goto_line(struct tog_view *view, int nlines)
+{
+	const struct got_error		 *err = NULL;
+	struct tog_tree_view_state	 *s = &view->state.tree;
+	struct got_tree_entry		**fte, **lte, **ste;
+	int				  g, last, first = 1, i = 1;
+	int				  root = s->tree == s->root;
+	int				  off = root ? 1 : 2;
+
+	g = view->gline;
+	view->gline = 0;
+
+	if (g == 0)
+		g = 1;
+	else if (g > got_object_tree_get_nentries(s->tree))
+		g = got_object_tree_get_nentries(s->tree) + (root ? 0 : 1);
+
+	fte = &s->first_displayed_entry;
+	lte = &s->last_displayed_entry;
+	ste = &s->selected_entry;
+
+	if (*fte != NULL) {
+		first = got_tree_entry_get_index(*fte);
+		first += off;  /* account for ".." */
+	}
+	last = got_tree_entry_get_index(*lte);
+	last += off;
+
+	if (g >= first && g <= last && g - first < nlines) {
+		s->selected = g - first;
+		return NULL;	/* gline is on the current page */
+	}
+
+	if (*ste != NULL) {
+		i = got_tree_entry_get_index(*ste);
+		i += off;
+	}
+
+	if (i < g) {
+		err = tree_scroll_down(view, g - i);
+		if (err)
+			return err;
+		if (got_tree_entry_get_index(*lte) >=
+		    got_object_tree_get_nentries(s->tree) - 1 &&
+		    first + s->selected < g &&
+		    s->selected < s->ndisplayed - 1) {
+			first = got_tree_entry_get_index(*fte);
+			first += off;
+			s->selected = g - first;
+		}
+	} else if (i > g)
+		tree_scroll_up(s, i - g);
+
+	if (g < nlines &&
+	    (*fte == NULL || (root && !got_tree_entry_get_index(*fte))))
+		s->selected = g - 1;
+
+	return NULL;
+}
+
+static const struct got_error *
 input_tree_view(struct tog_view **new_view, struct tog_view *view, int ch)
 {
 	const struct got_error *err = NULL;
@@ -6708,6 +6897,9 @@ input_tree_view(struct tog_view **new_view, struct tog
 	struct got_tree_entry *te;
 	int n, nscroll = view->nlines - 3;

+	if (view->gline)
+		return tree_goto_line(view, nscroll);
+
 	switch (ch) {
 	case 'i':
 		s->show_ids = !s->show_ids;
@@ -7269,8 +7461,10 @@ ref_scroll_down(struct tog_view *view, int maxscroll)

 	last = s->last_displayed_entry;
 	while (next && n++ < maxscroll) {
-		if (last)
+		if (last) {
+			s->last_displayed_entry = last;
 			last = TAILQ_NEXT(last, entry);
+		}
 		if (last || (view->mode == TOG_VIEW_SPLIT_HRZN)) {
 			s->first_displayed_entry = next;
 			next = TAILQ_NEXT(next, entry);
@@ -7551,7 +7745,48 @@ done:
 	free(commit_id);
 	return err;
 }
+
 static const struct got_error *
+ref_goto_line(struct tog_view *view, int nlines)
+{
+	const struct got_error		*err = NULL;
+	struct tog_ref_view_state	*s = &view->state.ref;
+	int				 g, idx = s->selected_entry->idx;
+
+	g = view->gline;
+	view->gline = 0;
+
+	if (g == 0)
+		g = 1;
+	else if (g > s->nrefs)
+		g = s->nrefs;
+
+	if (g >= s->first_displayed_entry->idx + 1 &&
+	    g <= s->last_displayed_entry->idx + 1 &&
+	    g - s->first_displayed_entry->idx - 1 < nlines) {
+		s->selected = g - s->first_displayed_entry->idx - 1;
+		return NULL;
+	}
+
+	if (idx + 1 < g) {
+		err = ref_scroll_down(view, g - idx - 1);
+		if (err)
+			return err;
+		if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL &&
+		    s->first_displayed_entry->idx + s->selected < g &&
+		    s->selected < s->ndisplayed - 1)
+			s->selected = g - s->first_displayed_entry->idx - 1;
+	} else if (idx + 1 > g)
+		ref_scroll_up(s, idx - g + 1);
+
+	if (g < nlines && s->first_displayed_entry->idx == 0)
+		s->selected = g - 1;
+
+	return NULL;
+
+}
+
+static const struct got_error *
 input_ref_view(struct tog_view **new_view, struct tog_view *view, int ch)
 {
 	const struct got_error *err = NULL;
@@ -7559,6 +7794,9 @@ input_ref_view(struct tog_view **new_view, struct tog_
 	struct tog_reflist_entry *re;
 	int n, nscroll = view->nlines - 1;

+	if (view->gline)
+		return ref_goto_line(view, nscroll);
+
 	switch (ch) {
 	case 'i':
 		s->show_ids = !s->show_ids;

-- 
Mark Jamsek <fnc.bsdbox.org>
GPG: F2FF 13DE 6A06 C471 CA80  E6E2 2930 DC66 86EE CF68