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

From:
Tracey Emery <tracey@traceyemery.net>
Subject:
Re: tog feature request: search
To:
Martin Pieuchot <mpi@openbsd.org>
Cc:
gameoftrees@openbsd.org
Date:
Fri, 31 Jan 2020 09:55:44 -0700

Download raw body.

Thread
On Thu, Jan 30, 2020 at 05:15:54PM +0100, Martin Pieuchot wrote:
> On 30/01/20(Thu) 08:43, Tracey Emery wrote:
> > On Thu, Jan 30, 2020 at 04:39:20PM +0100, Martin Pieuchot wrote:
> > > I'm lacking a 'search' functionality, when pressing '/', like in
> > > less(1).
> > > 
> > > In case anyone wants to give it a go :o)
> > 
> > I can '/' search in log view, but not in the diff view. That feature is
> > listed in the TODO. Is that where you're trying to search?
> 
> Yes :o)

"Tracey, would you please make some coffee?"

This is quick and I haven't gone through it thoroughly yet, but, do you
want to test it out? There is no highlighting yet, but I imagine that
could be added later.

:)
-- 

Tracey Emery

diff 1570c56fa1fc478f10a2d493693767f2b39c85d2 /home/basepr1me/Documents/got/got/got
blob - 461975117c98f1bb74df15d9317223d1940e4c6d
file + include/got_object.h
--- include/got_object.h
+++ include/got_object.h
@@ -258,6 +258,16 @@ const struct got_error *got_object_blob_dump_to_file(s
     off_t **, FILE *, struct got_blob_object *);
 
 /*
+ * Read the entire content of a blob and write it to the specified file.
+ * Flush and rewind the file as well. Indicate the amount of bytes
+ * written in the size_t output argument, and the number of lines in the
+ * file in the int argument, and line offsets in the off_t argument
+ * (NULL can be passed for any output argument).
+ */
+const struct got_error *got_object_file_get_info(size_t *, int *,
+    off_t **, FILE *);
+
+/*
  * Attempt to open a tag object in a repository.
  * The caller must dispose of the tree with got_tag_object_close().
  */
blob - 499c7c7c8d19381b481479ba9b78bab2fc685319
file + lib/object.c
--- lib/object.c
+++ lib/object.c
@@ -1284,6 +1284,91 @@ got_object_blob_dump_to_file(size_t *filesize, int *nl
 	return NULL;
 }
 
+const struct got_error *
+got_object_file_get_info(size_t *filesize, int *nlines,
+    off_t **line_offsets, FILE *infile)
+{
+	size_t len;
+	/* const uint8_t *buf = NULL; */
+	char *buf = NULL;
+	int i;
+	size_t noffsets = 0;
+	off_t off = 0, total_len = 0;
+
+	if (line_offsets)
+		*line_offsets = NULL;
+	if (filesize)
+		*filesize = 0;
+	if (nlines)
+		*nlines = 0;
+
+	fseek(infile, 0, SEEK_END);
+	len = ftell(infile) + 1;
+	fseek(infile, 0, SEEK_SET);
+	if (len == 0)
+		return NULL;
+	if ((buf = calloc(len, sizeof(char *))) == NULL)
+		return got_error_from_errno("calloc");
+	fread(buf, 1, len, infile);
+		i = 0;
+		if (line_offsets && nlines) {
+			if (*line_offsets == NULL) {
+				/* Have some data but perhaps no '\n'. */
+				noffsets = 1;
+				*nlines = 1;
+				*line_offsets = calloc(1, sizeof(**line_offsets));
+				if (*line_offsets == NULL)
+					return got_error_from_errno("malloc");
+
+				/* Skip forward over end of first line. */
+				while (i < len) {
+					if (buf[i] == '\n')
+						break;
+					i++;
+				}
+			}
+			/* Scan '\n' offsets in remaining chunk of data. */
+			while (i < len) {
+				if (buf[i] != '\n') {
+					i++;
+					continue;
+				}
+				(*nlines)++;
+				if (noffsets < *nlines) {
+					off_t *o = recallocarray(*line_offsets,
+					    noffsets, *nlines,
+					    sizeof(**line_offsets));
+					if (o == NULL) {
+						free(*line_offsets);
+						*line_offsets = NULL;
+						return got_error_from_errno(
+						    "recallocarray");
+					}
+					*line_offsets = o;
+					noffsets = *nlines;
+				}
+				off = total_len + i + 1;
+				(*line_offsets)[*nlines - 1] = off;
+				i++;
+			}
+		}
+		/* Skip blob object header first time around. */
+		/* n = fwrite(buf + hdrlen, 1, len - hdrlen, outfile); */
+		/* if (n != len - hdrlen) */
+		/* 	return got_ferror(outfile, GOT_ERR_IO); */
+		/* total_len += len; */
+		/* hdrlen = 0; */
+
+	if (fflush(infile) != 0)
+		return got_error_from_errno("fflush");
+	rewind(infile);
+
+	if (filesize)
+		*filesize = len;
+
+	return NULL;
+}
+
 static const struct got_error *
 request_packed_tag(struct got_tag_object **tag, struct got_pack *pack,
     int pack_idx, struct got_object_id *id)
blob - 57bbc95ef4f3fde8a3ffa7aed1ece2043bc143e7
file + tog/tog.c
--- tog/tog.c
+++ tog/tog.c
@@ -250,6 +250,11 @@ struct tog_diff_view_state {
 	struct got_repository *repo;
 	struct got_reflist_head *refs;
 	struct tog_colors colors;
+	int nlines;
+	off_t *line_offsets;
+	int matched_line;
+	int selected_line;
+	size_t filesize;
 
 	/* passed from log view; may be NULL */
 	struct tog_view *log_view;
@@ -440,6 +445,8 @@ static const struct got_error *show_diff_view(struct t
 static const struct got_error *input_diff_view(struct tog_view **,
     struct tog_view **, struct tog_view **, struct tog_view *, int);
 static const struct got_error* close_diff_view(struct tog_view *);
+static const struct got_error *search_start_diff_view(struct tog_view *);
+static const struct got_error *search_next_diff_view(struct tog_view *);
 
 static const struct got_error *open_log_view(struct tog_view *,
     struct got_object_id *, struct got_reflist_head *,
@@ -2679,12 +2686,12 @@ match_color(struct tog_colors *colors, const char *lin
 }
 
 static const struct got_error *
-draw_file(struct tog_view *view, FILE *f, int *first_displayed_line,
-    int *last_displayed_line, int *eof, int max_lines, char *header,
-    struct tog_colors *colors)
+draw_file(struct tog_view *view, FILE *f, int *first_displayed_line, int nlines,
+    int selected_line, int max_lines, int *last_displayed_line, int *eof,
+    char *header, struct tog_colors *colors)
 {
 	const struct got_error *err;
-	int nlines = 0, nprinted = 0;
+	int lineno = 0, nprinted = 0;
 	char *line;
 	struct tog_color *tc;
 	size_t len;
@@ -2720,7 +2727,7 @@ draw_file(struct tog_view *view, FILE *f, int *first_d
 			*eof = 1;
 			break;
 		}
-		if (++nlines < *first_displayed_line) {
+		if (++lineno < *first_displayed_line) {
 			free(line);
 			continue;
 		}
@@ -2742,12 +2749,12 @@ draw_file(struct tog_view *view, FILE *f, int *first_d
 		if (width <= view->ncols - 1)
 			waddch(view->window, '\n');
 		if (++nprinted == 1)
-			*first_displayed_line = nlines;
+			*first_displayed_line = lineno;
 		free(line);
 		free(wline);
 		wline = NULL;
 	}
-	*last_displayed_line = nlines;
+	*last_displayed_line = lineno;
 
 	view_vborder(view);
 
@@ -2858,19 +2865,13 @@ static const struct got_error *
 create_diff(struct tog_diff_view_state *s)
 {
 	const struct got_error *err = NULL;
-	FILE *f = NULL;
 	int obj_type;
 
-	f = got_opentemp();
-	if (f == NULL) {
+	s->f = got_opentemp();
+	if (s->f == NULL) {
 		err = got_error_from_errno("got_opentemp");
 		goto done;
 	}
-	if (s->f && fclose(s->f) != 0) {
-		err = got_error_from_errno("fclose");
-		goto done;
-	}
-	s->f = f;
 
 	if (s->id1)
 		err = got_object_get_type(&obj_type, s->repo, s->id1);
@@ -2882,11 +2883,11 @@ create_diff(struct tog_diff_view_state *s)
 	switch (obj_type) {
 	case GOT_OBJ_TYPE_BLOB:
 		err = got_diff_objects_as_blobs(s->id1, s->id2, NULL, NULL,
-		    s->diff_context, 0, s->repo, f);
+		    s->diff_context, 0, s->repo, s->f);
 		break;
 	case GOT_OBJ_TYPE_TREE:
 		err = got_diff_objects_as_trees(s->id1, s->id2, "", "",
-		    s->diff_context, 0, s->repo, f);
+		    s->diff_context, 0, s->repo, s->f);
 		break;
 	case GOT_OBJ_TYPE_COMMIT: {
 		const struct got_object_id_queue *parent_ids;
@@ -2898,13 +2899,13 @@ create_diff(struct tog_diff_view_state *s)
 			break;
 		/* Show commit info if we're diffing to a parent/root commit. */
 		if (s->id1 == NULL)
-			write_commit_info(s->id2, s->refs, s->repo, f);
+			write_commit_info(s->id2, s->refs, s->repo, s->f);
 		else {
 			parent_ids = got_object_commit_get_parent_ids(commit2);
 			SIMPLEQ_FOREACH(pid, parent_ids, entry) {
 				if (got_object_id_cmp(s->id1, pid->id) == 0) {
 					write_commit_info(s->id2, s->refs,
-					    s->repo, f);
+					    s->repo, s->f);
 					break;
 				}
 			}
@@ -2912,15 +2913,17 @@ create_diff(struct tog_diff_view_state *s)
 		got_object_commit_close(commit2);
 
 		err = got_diff_objects_as_commits(s->id1, s->id2,
-		    s->diff_context, 0, s->repo, f);
+		    s->diff_context, 0, s->repo, s->f);
 		break;
 	}
 	default:
 		err = got_error(GOT_ERR_OBJ_TYPE);
 		break;
 	}
+	err = got_object_file_get_info(&s->filesize, &s->nlines,
+	    &s->line_offsets, s->f);
 done:
-	if (f && fflush(f) != 0 && err == NULL)
+	if (s->f && fflush(s->f) != 0 && err == NULL)
 		err = got_error_from_errno("fflush");
 	return err;
 }
@@ -2934,11 +2937,90 @@ diff_view_indicate_progress(struct tog_view *view)
 }
 
 static const struct got_error *
+search_start_diff_view(struct tog_view *view)
+{
+	struct tog_diff_view_state *s = &view->state.diff;
+
+	s->matched_line = 0;
+	return NULL;
+}
+
+static const struct got_error *
+search_next_diff_view(struct tog_view *view)
+{
+	struct tog_diff_view_state *s = &view->state.diff;
+	int lineno;
+
+	if (!view->searching) {
+		view->search_next_done = 1;
+		return NULL;
+	}
+
+	if (s->matched_line) {
+		if (view->searching == TOG_SEARCH_FORWARD)
+			lineno = s->matched_line + 1;
+		else
+			lineno = s->matched_line - 1;
+	} else {
+		if (view->searching == TOG_SEARCH_FORWARD)
+			lineno = 1;
+		else
+			lineno = s->nlines;
+	}
+
+	while (1) {
+		char *line = NULL;
+		off_t offset;
+		size_t len;
+
+		if (lineno <= 0 || lineno > s->nlines) {
+			if (s->matched_line == 0) {
+				view->search_next_done = 1;
+				free(line);
+				break;
+			}
+
+			if (view->searching == TOG_SEARCH_FORWARD)
+				lineno = 1;
+			else
+				lineno = s->nlines;
+		}
+
+		offset = s->line_offsets[lineno - 1];
+		if (fseeko(s->f, offset, SEEK_SET) != 0) {
+			free(line);
+			return got_error_from_errno("fseeko");
+		}
+		free(line);
+		line = parse_next_line(s->f, &len);
+		if (line && match_line(line, &view->regex)) {
+			view->search_next_done = 1;
+			s->matched_line = lineno;
+			free(line);
+			break;
+		}
+		free(line);
+		if (view->searching == TOG_SEARCH_FORWARD)
+			lineno++;
+		else
+			lineno--;
+	}
+
+	if (s->matched_line) {
+		s->first_displayed_line = s->matched_line;
+		s->selected_line = 1;
+	}
+
+	return NULL;
+}
+
+static const struct got_error *
 open_diff_view(struct tog_view *view, struct got_object_id *id1,
     struct got_object_id *id2, struct tog_view *log_view,
     struct got_reflist_head *refs, struct got_repository *repo)
 {
 	const struct got_error *err;
+	struct tog_diff_view_state *s = &view->state.diff;
 
 	if (id1 != NULL && id2 != NULL) {
 	    int type1, type2;
@@ -2952,71 +3034,78 @@ open_diff_view(struct tog_view *view, struct got_objec
 	    if (type1 != type2)
 		return got_error(GOT_ERR_OBJ_TYPE);
 	}
+	s->first_displayed_line = 1;
+	s->last_displayed_line = view->nlines;
+	s->selected_line = 1;
+	s->repo = repo;
+	s->refs = refs;
+	s->id1 = id1;
+	s->id2 = id2;
 
 	if (id1) {
-		view->state.diff.id1 = got_object_id_dup(id1);
-		if (view->state.diff.id1 == NULL)
+		s->id1 = got_object_id_dup(id1);
+		if (s->id1 == NULL)
 			return got_error_from_errno("got_object_id_dup");
 	} else
-		view->state.diff.id1 = NULL;
+		s->id1 = NULL;
 
-	view->state.diff.id2 = got_object_id_dup(id2);
-	if (view->state.diff.id2 == NULL) {
-		free(view->state.diff.id1);
-		view->state.diff.id1 = NULL;
+	s->id2 = got_object_id_dup(id2);
+	if (s->id2 == NULL) {
+		free(s->id1);
+		s->id1 = NULL;
 		return got_error_from_errno("got_object_id_dup");
 	}
-	view->state.diff.f = NULL;
-	view->state.diff.first_displayed_line = 1;
-	view->state.diff.last_displayed_line = view->nlines;
-	view->state.diff.diff_context = 3;
-	view->state.diff.log_view = log_view;
-	view->state.diff.repo = repo;
-	view->state.diff.refs = refs;
-	SIMPLEQ_INIT(&view->state.diff.colors);
+	s->f = NULL;
+	s->first_displayed_line = 1;
+	s->last_displayed_line = view->nlines;
+	s->diff_context = 3;
+	s->log_view = log_view;
+	s->repo = repo;
+	s->refs = refs;
 
+	SIMPLEQ_INIT(&s->colors);
 	if (has_colors() && getenv("TOG_COLORS") != NULL) {
-		err = add_color(&view->state.diff.colors,
+		err = add_color(&s->colors,
 		    "^-", TOG_COLOR_DIFF_MINUS,
 		    get_color_value("TOG_COLOR_DIFF_MINUS"));
 		if (err)
 			return err;
-		err = add_color(&view->state.diff.colors, "^\\+",
+		err = add_color(&s->colors, "^\\+",
 		    TOG_COLOR_DIFF_PLUS,
 		    get_color_value("TOG_COLOR_DIFF_PLUS"));
 		if (err) {
-			free_colors(&view->state.diff.colors);
+			free_colors(&s->colors);
 			return err;
 		}
-		err = add_color(&view->state.diff.colors, 
+		err = add_color(&s->colors,
 		    "^@@", TOG_COLOR_DIFF_CHUNK_HEADER,
 		    get_color_value("TOG_COLOR_DIFF_CHUNK_HEADER"));
 		if (err) {
-			free_colors(&view->state.diff.colors);
+			free_colors(&s->colors);
 			return err;
 		}
 
-		err = add_color(&view->state.diff.colors, 
+		err = add_color(&s->colors,
 		    "^(commit|(blob|file) [-+] )", TOG_COLOR_DIFF_META,
 		    get_color_value("TOG_COLOR_DIFF_META"));
 		if (err) {
-			free_colors(&view->state.diff.colors);
+			free_colors(&s->colors);
 			return err;
 		}
 
-		err = add_color(&view->state.diff.colors, 
+		err = add_color(&s->colors,
 		    "^(from|via): ", TOG_COLOR_AUTHOR,
 		    get_color_value("TOG_COLOR_AUTHOR"));
 		if (err) {
-			free_colors(&view->state.diff.colors);
+			free_colors(&s->colors);
 			return err;
 		}
 
-		err = add_color(&view->state.diff.colors, 
+		err = add_color(&s->colors,
 		    "^date: ", TOG_COLOR_DATE,
 		    get_color_value("TOG_COLOR_DATE"));
 		if (err) {
-			free_colors(&view->state.diff.colors);
+			free_colors(&s->colors);
 			return err;
 		}
 	}
@@ -3025,18 +3114,20 @@ open_diff_view(struct tog_view *view, struct got_objec
 		show_log_view(log_view); /* draw vborder */
 	diff_view_indicate_progress(view);
 
-	err = create_diff(&view->state.diff);
+	err = create_diff(s);
 	if (err) {
-		free(view->state.diff.id1);
-		view->state.diff.id1 = NULL;
-		free(view->state.diff.id2);
-		view->state.diff.id2 = NULL;
+		free(s->id1);
+		s->id1 = NULL;
+		free(s->id2);
+		s->id2 = NULL;
 		return err;
 	}
 
 	view->show = show_diff_view;
 	view->input = input_diff_view;
 	view->close = close_diff_view;
+	view->search_start = search_start_diff_view;
+	view->search_next = search_next_diff_view;
 
 	return NULL;
 }
@@ -3045,14 +3136,15 @@ static const struct got_error *
 close_diff_view(struct tog_view *view)
 {
 	const struct got_error *err = NULL;
+	struct tog_diff_view_state *s = &view->state.diff;
 
-	free(view->state.diff.id1);
-	view->state.diff.id1 = NULL;
-	free(view->state.diff.id2);
-	view->state.diff.id2 = NULL;
-	if (view->state.diff.f && fclose(view->state.diff.f) == EOF)
+	free(s->id1);
+	s->id1 = NULL;
+	free(s->id2);
+	s->id2 = NULL;
+	if (s->f && fclose(s->f) == EOF)
 		err = got_error_from_errno("fclose");
-	free_colors(&view->state.diff.colors);
+	free_colors(&s->colors);
 	return err;
 }
 
@@ -3082,8 +3174,8 @@ show_diff_view(struct tog_view *view)
 	free(id_str1);
 	free(id_str2);
 
-	return draw_file(view, s->f, &s->first_displayed_line,
-	    &s->last_displayed_line, &s->eof, view->nlines,
+	return draw_file(view, s->f, &s->first_displayed_line, s->nlines,
+	    s->selected_line, view->nlines, &s->last_displayed_line, &s->eof,
 	    header, &s->colors);
 }