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

From:
Mark Jamsek <mark@jamsek.com>
Subject:
tog: implement diff view 'p' keymap to write patch file
To:
gameoftrees@openbsd.org
Date:
Sat, 10 Aug 2024 00:55:12 +1000

Download raw body.

Thread
As mentioned in the log view 'm' keymap thread, this is the next step to
facilitate code review workflows with a new 'p' keymap to write out the
currently viewed diff to a patch file.

We write to a file in /tmp and report the path to the status line. In
contrast to existing action reports that are only displayed momentarily,
the report sticks to the status line until the user scrolls, closes, or
changes the diff view. I think this is important to allow users to copy
the path if they don't want to rely on tab completion in the shell in
the event they have written out several patch files.

One thing I quite like is the patch respects runtime knobs. So if
context lines have been increased to 5, the diff in the patch file has
5 context lines; if the diff algorithm is changed or whitespace is
ignored, this is reflected in the patch file. It's a nice shortcut to
generating diffs with the CLI.

As suggested by stsp, the next step after writing tests for this is to
enable writing out a git-format-patch style file with the 'P' keymap.


-----------------------------------------------
commit 7c2ba31d44ea47bfec058e68e4d25d8540ebba9f (main)
from: Mark Jamsek <mark@jamsek.dev>
date: Fri Aug  9 14:45:51 2024 UTC
 
 tog: add diff view 'p' keymap to write out a patch file
 
 Write the current diff view to a temporary patch file in /tmp
 and report the patch file pathname to the status line.
 
 M  tog/tog.1  |   4+  0-
 M  tog/tog.c  |  81+  0-

2 files changed, 85 insertions(+), 0 deletions(-)

diff 3271ec93b5ca1c58fef468135fe380819cef6e9e 7c2ba31d44ea47bfec058e68e4d25d8540ebba9f
commit - 3271ec93b5ca1c58fef468135fe380819cef6e9e
commit + 7c2ba31d44ea47bfec058e68e4d25d8540ebba9f
blob - 0f8f83ded2fa834467a0c0bef5de29b3578ddcaa
blob + 6e0e4df78865c58469ef88303ebfa3222886178f
--- tog/tog.1
+++ tog/tog.1
@@ -455,6 +455,10 @@ view, move to the Nth next (older) commit.
 If the diff was opened via the
 .Cm blame
 view, move to the Nth next line and load the corresponding commit (default: 1).
+.It Cm p
+Write the currently viewed diff to a patch file in
+.Pa /tmp .
+The patch pathname is drawn to the status line.
 .It Cm /
 Prompt for a search pattern and start searching for matching lines.
 The search pattern is an extended regular expression.
blob - dbbaf92a90b365370c465e5011b8410a7a8d6c18
blob + 13586fd308da97f395a46278d336699dc1aa3eac
--- tog/tog.c
+++ tog/tog.c
@@ -336,6 +336,7 @@ get_color_value(const char *envvar)
 struct tog_diff_view_state {
 	struct got_object_id *id1, *id2;
 	const char *label1, *label2;
+	char *action;
 	FILE *f, *f1, *f2;
 	int fd1, fd2;
 	int lineno;
@@ -585,6 +586,7 @@ struct tog_help_view_state {
 	KEY_("A", "Toggle between Myers and Patience diff algorithm"), \
 	KEY_("a", "Toggle treatment of file as ASCII irrespective of binary" \
 	    " data"), \
+	KEY_("p", "Write diff to a patch file in /tmp"), \
 	KEY_("(", "Go to the previous file in the diff"), \
 	KEY_(")", "Go to the next file in the diff"), \
 	KEY_("{", "Go to the previous hunk in the diff"), \
@@ -1620,10 +1622,13 @@ action_report(struct tog_view *view)
 	/*
 	 * Clear action status report. Only clear in blame view
 	 * once annotating is complete, otherwise it's too fast.
+	 * In diff view, let its state control view->action lifetime.
 	 */
 	if (view->type == TOG_VIEW_BLAME) {
 		if (view->state.blame.blame_complete)
 			view->action = NULL;
+	} else if (view->type == TOG_VIEW_DIFF) {
+		view->action = view->state.diff.action;
 	} else
 		view->action = NULL;
 }
@@ -5616,6 +5621,8 @@ close_diff_view(struct tog_view *view)
 	s->id1 = NULL;
 	free(s->id2);
 	s->id2 = NULL;
+	free(s->action);
+	s->action = NULL;
 	if (s->f && fclose(s->f) == EOF)
 		err = got_error_from_errno("fclose");
 	s->f = NULL;
@@ -5811,6 +5818,67 @@ show_diff_view(struct tog_view *view)
 }
 
 static const struct got_error *
+diff_write_patch(struct tog_view *view)
+{
+	const struct got_error		*err;
+	struct tog_diff_view_state	*s = &view->state.diff;
+	FILE				*f;
+	char				 buf[BUFSIZ];
+	char				*path;
+	size_t				 r;
+	off_t				 pos;
+
+	if (s->action != NULL) {
+		free(s->action);
+		s->action = NULL;
+	}
+
+	pos = ftello(s->f);
+	if (pos == -1)
+		return got_error_from_errno("ftello");
+	if (fseeko(s->f, 0L, SEEK_SET) == -1)
+		return got_error_from_errno("fseeko");
+
+	err = got_opentemp_named(&path, &f, GOT_TMPDIR_STR "/tog", ".diff");
+	if (err != NULL)
+		return err;
+
+	while ((r = fread(buf, 1, sizeof(buf), s->f)) > 0) {
+		if (fwrite(buf, 1, r, f) != r) {
+			err = got_ferror(f, GOT_ERR_IO);
+			goto done;
+		}
+	}
+
+	if (ferror(s->f)) {
+		err = got_error_from_errno("fread");
+		goto done;
+	}
+	if (fseeko(s->f, pos, SEEK_SET) == -1) {
+		err = got_error_from_errno("fseeko");
+		goto done;
+	}
+
+	if (fflush(f) == EOF) {
+		err = got_error_from_errno2("fflush", path);
+		goto done;
+	}
+
+	if (asprintf(&s->action, "patch file written to %s", path) == -1) {
+		err = got_error_from_errno("asprintf");
+		goto done;
+	}
+
+	view->action = s->action;
+
+done:
+	if (f != NULL && fclose(f) == EOF && err == NULL)
+		err = got_error_from_errno2("fclose", path);
+	free(path);
+	return err;
+}
+
+static const struct got_error *
 set_selected_commit(struct tog_diff_view_state *s,
     struct commit_queue_entry *entry)
 {
@@ -5845,6 +5913,10 @@ reset_diff_view(struct tog_view *view)
 	s->first_displayed_line = 1;
 	s->last_displayed_line = view->nlines;
 	s->matched_line = 0;
+	if (s->action != NULL) {
+		free(s->action);
+		s->action = NULL;
+	}
 	diff_view_indicate_progress(view);
 	return create_diff(s);
 }
@@ -5904,6 +5976,12 @@ input_diff_view(struct tog_view **new_view, struct tog
 
 	s->lineno = s->first_displayed_line - 1 + s->selected_line;
 
+	if (s->action != NULL && ch != ERR) {
+		free(s->action);
+		s->action = NULL;
+		view->action = NULL;
+	}
+
 	switch (ch) {
 	case '0':
 	case '$':
@@ -6104,6 +6182,9 @@ input_diff_view(struct tog_view **new_view, struct tog
 		diff_view_indicate_progress(view);
 		err = create_diff(s);
 		break;
+	case 'p':
+		err = diff_write_patch(view);
+		break;
 	default:
 		view->count = 0;
 		break;


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