From: Tracey Emery Subject: tog diff view search To: gameoftrees@openbsd.org Date: Fri, 31 Jan 2020 10:49:20 -0700 Going to get this in its own thread. Below is a diff to allow searching in tog diff view. This does not currently highlight search terms. Thoughts? What did I miss? What needs fixed? Not sure I like the new function name either. -- Tracey Emery diff de6bdba4416e9e727ac8933082ccb83b56fbd3ab /home/basepr1me/Documents/got/got/got blob - 461975117c98f1bb74df15d9317223d1940e4c6d file + include/got_object.h --- include/got_object.h +++ include/got_object.h @@ -258,6 +258,15 @@ const struct got_error *got_object_blob_dump_to_file(s off_t **, FILE *, struct got_blob_object *); /* + * Read the contents of a FILE stream and Indicate the amount of bytes + * written in the size_t output argument, the number of lines in the + * file in the int argument, and line offsets in the off_t argument, + * then flush and rewind the file. + */ +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); }