Add reflog member to the column_data struct
[tig.git] / src / view.c
blob45257440c2f2392654555704a2fe91cd097156e2
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/argv.h"
16 #include "tig/repo.h"
17 #include "tig/options.h"
18 #include "tig/view.h"
19 #include "tig/draw.h"
20 #include "tig/display.h"
23 * Navigation
26 bool
27 goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
29 if (lineno >= view->lines)
30 lineno = view->lines > 0 ? view->lines - 1 : 0;
32 if (offset > lineno || offset + view->height <= lineno) {
33 unsigned long half = view->height / 2;
35 if (lineno > half)
36 offset = lineno - half;
37 else
38 offset = 0;
41 if (offset != view->pos.offset || lineno != view->pos.lineno) {
42 view->pos.offset = offset;
43 view->pos.lineno = lineno;
44 return TRUE;
47 return FALSE;
50 /* Scrolling backend */
51 void
52 do_scroll_view(struct view *view, int lines)
54 bool redraw_current_line = FALSE;
56 /* The rendering expects the new offset. */
57 view->pos.offset += lines;
59 assert(0 <= view->pos.offset && view->pos.offset < view->lines);
60 assert(lines);
62 /* Move current line into the view. */
63 if (view->pos.lineno < view->pos.offset) {
64 view->pos.lineno = view->pos.offset;
65 redraw_current_line = TRUE;
66 } else if (view->pos.lineno >= view->pos.offset + view->height) {
67 view->pos.lineno = view->pos.offset + view->height - 1;
68 redraw_current_line = TRUE;
71 assert(view->pos.offset <= view->pos.lineno && view->pos.lineno < view->lines);
73 /* Redraw the whole screen if scrolling is pointless. */
74 if (view->height < ABS(lines)) {
75 redraw_view(view);
77 } else {
78 int line = lines > 0 ? view->height - lines : 0;
79 int end = line + ABS(lines);
81 scrollok(view->win, TRUE);
82 wscrl(view->win, lines);
83 scrollok(view->win, FALSE);
85 while (line < end && draw_view_line(view, line))
86 line++;
88 if (redraw_current_line)
89 draw_view_line(view, view->pos.lineno - view->pos.offset);
90 wnoutrefresh(view->win);
93 view->has_scrolled = TRUE;
94 report_clear();
97 /* Scroll frontend */
98 void
99 scroll_view(struct view *view, enum request request)
101 int lines = 1;
103 assert(view_is_displayed(view));
105 if (request == REQ_SCROLL_WHEEL_DOWN || request == REQ_SCROLL_WHEEL_UP)
106 lines = opt_mouse_scroll;
108 switch (request) {
109 case REQ_SCROLL_FIRST_COL:
110 view->pos.col = 0;
111 redraw_view_from(view, 0);
112 report_clear();
113 return;
114 case REQ_SCROLL_LEFT:
115 if (view->pos.col == 0) {
116 report("Cannot scroll beyond the first column");
117 return;
119 if (view->pos.col <= apply_step(opt_horizontal_scroll, view->width))
120 view->pos.col = 0;
121 else
122 view->pos.col -= apply_step(opt_horizontal_scroll, view->width);
123 redraw_view_from(view, 0);
124 report_clear();
125 return;
126 case REQ_SCROLL_RIGHT:
127 view->pos.col += apply_step(opt_horizontal_scroll, view->width);
128 redraw_view(view);
129 report_clear();
130 return;
131 case REQ_SCROLL_PAGE_DOWN:
132 lines = view->height;
133 case REQ_SCROLL_WHEEL_DOWN:
134 case REQ_SCROLL_LINE_DOWN:
135 if (view->pos.offset + lines > view->lines)
136 lines = view->lines - view->pos.offset;
138 if (lines == 0 || view->pos.offset + view->height >= view->lines) {
139 report("Cannot scroll beyond the last line");
140 return;
142 break;
144 case REQ_SCROLL_PAGE_UP:
145 lines = view->height;
146 case REQ_SCROLL_LINE_UP:
147 case REQ_SCROLL_WHEEL_UP:
148 if (lines > view->pos.offset)
149 lines = view->pos.offset;
151 if (lines == 0) {
152 report("Cannot scroll beyond the first line");
153 return;
156 lines = -lines;
157 break;
159 default:
160 die("request %d not handled in switch", request);
163 do_scroll_view(view, lines);
166 /* Cursor moving */
167 void
168 move_view(struct view *view, enum request request)
170 int scroll_steps = 0;
171 int steps;
173 switch (request) {
174 case REQ_MOVE_FIRST_LINE:
175 steps = -view->pos.lineno;
176 break;
178 case REQ_MOVE_LAST_LINE:
179 steps = view->lines - view->pos.lineno - 1;
180 break;
182 case REQ_MOVE_PAGE_UP:
183 steps = view->height > view->pos.lineno
184 ? -view->pos.lineno : -view->height;
185 break;
187 case REQ_MOVE_PAGE_DOWN:
188 steps = view->pos.lineno + view->height >= view->lines
189 ? view->lines - view->pos.lineno - 1 : view->height;
190 break;
192 case REQ_MOVE_UP:
193 case REQ_PREVIOUS:
194 steps = -1;
195 break;
197 case REQ_MOVE_DOWN:
198 case REQ_NEXT:
199 steps = 1;
200 break;
202 default:
203 die("request %d not handled in switch", request);
206 if (steps <= 0 && view->pos.lineno == 0) {
207 report("Cannot move beyond the first line");
208 return;
210 } else if (steps >= 0 && view->pos.lineno + 1 >= view->lines) {
211 report("Cannot move beyond the last line");
212 return;
215 /* Move the current line */
216 view->pos.lineno += steps;
217 assert(0 <= view->pos.lineno && view->pos.lineno < view->lines);
219 /* Check whether the view needs to be scrolled */
220 if (view->pos.lineno < view->pos.offset ||
221 view->pos.lineno >= view->pos.offset + view->height) {
222 scroll_steps = steps;
223 if (steps < 0 && -steps > view->pos.offset) {
224 scroll_steps = -view->pos.offset;
226 } else if (steps > 0) {
227 if (view->pos.lineno == view->lines - 1 &&
228 view->lines > view->height) {
229 scroll_steps = view->lines - view->pos.offset - 1;
230 if (scroll_steps >= view->height)
231 scroll_steps -= view->height - 1;
236 if (!view_is_displayed(view)) {
237 view->pos.offset += scroll_steps;
238 assert(0 <= view->pos.offset && view->pos.offset < view->lines);
239 view->ops->select(view, &view->line[view->pos.lineno]);
240 return;
243 /* Repaint the old "current" line if we be scrolling */
244 if (ABS(steps) < view->height)
245 draw_view_line(view, view->pos.lineno - steps - view->pos.offset);
247 if (scroll_steps) {
248 do_scroll_view(view, scroll_steps);
249 return;
252 /* Draw the current line */
253 draw_view_line(view, view->pos.lineno - view->pos.offset);
255 wnoutrefresh(view->win);
256 report_clear();
260 * Searching
263 DEFINE_ALLOCATOR(realloc_unsigned_ints, unsigned int, 32)
265 bool
266 grep_text(struct view *view, const char *text[])
268 regmatch_t pmatch;
269 size_t i;
271 for (i = 0; text[i]; i++)
272 if (*text[i] && !regexec(view->regex, text[i], 1, &pmatch, 0))
273 return TRUE;
274 return FALSE;
277 void
278 select_view_line(struct view *view, unsigned long lineno)
280 struct position old = view->pos;
282 if (goto_view_line(view, view->pos.offset, lineno)) {
283 if (view_is_displayed(view)) {
284 if (old.offset != view->pos.offset) {
285 redraw_view(view);
286 } else {
287 draw_view_line(view, old.lineno - view->pos.offset);
288 draw_view_line(view, view->pos.lineno - view->pos.offset);
289 wnoutrefresh(view->win);
291 } else {
292 view->ops->select(view, &view->line[view->pos.lineno]);
297 static bool
298 find_matches(struct view *view)
300 size_t lineno;
302 /* Note, lineno is unsigned long so will wrap around in which case it
303 * will become bigger than view->lines. */
304 for (lineno = 0; lineno < view->lines; lineno++) {
305 if (!view->ops->grep(view, &view->line[lineno]))
306 continue;
308 if (!realloc_unsigned_ints(&view->matched_line, view->matched_lines, 1))
309 return FALSE;
311 view->matched_line[view->matched_lines++] = lineno;
314 return TRUE;
317 void
318 find_next(struct view *view, enum request request)
320 int direction;
321 size_t i;
323 if (!*view->grep) {
324 if (!*view->env->search)
325 report("No previous search");
326 else
327 search_view(view, request);
328 return;
331 switch (request) {
332 case REQ_SEARCH:
333 case REQ_FIND_NEXT:
334 direction = 1;
335 break;
337 case REQ_SEARCH_BACK:
338 case REQ_FIND_PREV:
339 direction = -1;
340 break;
342 default:
343 return;
346 if (!view->matched_lines && !find_matches(view)) {
347 report("Allocation failure");
348 return;
351 /* Note, `i` is unsigned and will wrap around in which case it
352 * will become bigger than view->matched_lines. */
353 i = direction > 0 ? 0 : view->matched_lines - 1;
354 for (; i < view->matched_lines; i += direction) {
355 size_t lineno = view->matched_line[i];
357 if (direction > 0 && lineno <= view->pos.lineno)
358 continue;
360 if (direction < 0 && lineno >= view->pos.lineno)
361 continue;
363 select_view_line(view, lineno);
364 report("Line %zu matches '%s' (%zu of %zu)", lineno + 1, view->grep, i + 1, view->matched_lines);
365 return;
368 report("No match found for '%s'", view->grep);
371 static void
372 reset_matches(struct view *view)
374 free(view->matched_line);
375 view->matched_line = NULL;
376 view->matched_lines = 0;
379 void
380 search_view(struct view *view, enum request request)
382 int regex_err;
383 int regex_flags = opt_ignore_case ? REG_ICASE : 0;
385 if (view->regex) {
386 regfree(view->regex);
387 *view->grep = 0;
388 } else {
389 view->regex = calloc(1, sizeof(*view->regex));
390 if (!view->regex)
391 return;
394 regex_err = regcomp(view->regex, view->env->search, REG_EXTENDED | regex_flags);
395 if (regex_err != 0) {
396 char buf[SIZEOF_STR] = "unknown error";
398 regerror(regex_err, view->regex, buf, sizeof(buf));
399 report("Search failed: %s", buf);
400 return;
403 string_copy(view->grep, view->env->search);
405 reset_matches(view);
407 find_next(view, request);
411 * View history
414 static bool
415 view_history_is_empty(struct view_history *history)
417 return !history->stack;
420 struct view_state *
421 push_view_history_state(struct view_history *history, struct position *position, void *data)
423 struct view_state *state = history->stack;
425 if (state && data && history->state_alloc &&
426 !memcmp(state->data, data, history->state_alloc))
427 return NULL;
429 state = calloc(1, sizeof(*state) + history->state_alloc);
430 if (!state)
431 return NULL;
433 state->prev = history->stack;
434 history->stack = state;
435 clear_position(&history->position);
436 state->position = *position;
437 state->data = &state[1];
438 if (data && history->state_alloc)
439 memcpy(state->data, data, history->state_alloc);
440 return state;
443 bool
444 pop_view_history_state(struct view_history *history, struct position *position, void *data)
446 struct view_state *state = history->stack;
448 if (view_history_is_empty(history))
449 return FALSE;
451 history->position = state->position;
452 history->stack = state->prev;
454 if (data && history->state_alloc)
455 memcpy(data, state->data, history->state_alloc);
456 if (position)
457 *position = state->position;
459 free(state);
460 return TRUE;
463 void
464 reset_view_history(struct view_history *history)
466 while (pop_view_history_state(history, NULL, NULL))
471 * Incremental updating
474 void
475 reset_view(struct view *view)
477 int i;
479 for (i = 0; i < view->lines; i++)
480 free(view->line[i].data);
481 free(view->line);
483 reset_matches(view);
484 view->prev_pos = view->pos;
485 clear_position(&view->pos);
487 if (view->columns)
488 view_column_reset(view);
490 view->line = NULL;
491 view->lines = 0;
492 view->vid[0] = 0;
493 view->custom_lines = 0;
494 view->update_secs = 0;
497 static bool
498 restore_view_position(struct view *view)
500 /* A view without a previous view is the first view */
501 if (!view->prev && view->env->lineno && view->env->lineno <= view->lines) {
502 select_view_line(view, view->env->lineno);
503 view->env->lineno = 0;
506 /* Ensure that the view position is in a valid state. */
507 if (!check_position(&view->prev_pos) ||
508 (view->pipe && view->lines <= view->prev_pos.lineno))
509 return goto_view_line(view, view->pos.offset, view->pos.lineno);
511 /* Changing the view position cancels the restoring. */
512 /* FIXME: Changing back to the first line is not detected. */
513 if (check_position(&view->pos)) {
514 clear_position(&view->prev_pos);
515 return FALSE;
518 if (goto_view_line(view, view->prev_pos.offset, view->prev_pos.lineno) &&
519 view_is_displayed(view))
520 werase(view->win);
522 view->pos.col = view->prev_pos.col;
523 clear_position(&view->prev_pos);
525 return TRUE;
528 void
529 end_update(struct view *view, bool force)
531 if (!view->pipe)
532 return;
533 while (!view->ops->read(view, NULL))
534 if (!force)
535 return;
536 if (force)
537 io_kill(view->pipe);
538 io_done(view->pipe);
539 view->pipe = NULL;
542 static void
543 setup_update(struct view *view, const char *vid)
545 reset_view(view);
546 /* XXX: Do not use string_copy_rev(), it copies until first space. */
547 string_ncopy(view->vid, vid, strlen(vid));
548 view->pipe = &view->io;
549 view->start_time = time(NULL);
552 static bool
553 view_no_refresh(struct view *view, enum open_flags flags)
555 bool reload = !!(flags & OPEN_ALWAYS_LOAD) || !view->lines;
557 return (!reload && !strcmp(view->vid, view->ops->id)) ||
558 ((flags & OPEN_REFRESH) && view->unrefreshable);
561 bool
562 begin_update(struct view *view, const char *dir, const char **argv, enum open_flags flags)
564 bool extra = !!(flags & (OPEN_EXTRA));
565 bool refresh = flags & (OPEN_REFRESH | OPEN_PREPARED | OPEN_STDIN);
566 int forward_stdin = (flags & OPEN_FORWARD_STDIN) ? IO_RD_FORWARD_STDIN : 0;
567 int with_stderr = (flags & OPEN_WITH_STDERR) ? IO_RD_WITH_STDERR : 0;
568 int io_flags = forward_stdin | with_stderr;
570 if (view_no_refresh(view, flags))
571 return TRUE;
573 if (view->pipe) {
574 if (extra)
575 io_done(view->pipe);
576 else
577 end_update(view, TRUE);
580 view->unrefreshable = open_in_pager_mode(flags);
582 if (!refresh && argv) {
583 bool file_filter = !view_has_flags(view, VIEW_FILE_FILTER) || opt_file_filter;
585 view->dir = dir;
586 if (!argv_format(view->env, &view->argv, argv, !view->prev, file_filter)) {
587 report("Failed to format %s arguments", view->name);
588 return FALSE;
591 /* Put the current view ref value to the view title ref
592 * member. This is needed by the blob view. Most other
593 * views sets it automatically after loading because the
594 * first line is a commit line. */
595 string_copy_rev(view->ref, view->ops->id);
598 if (view->argv && view->argv[0] &&
599 !io_exec(&view->io, IO_RD, view->dir, opt_env, view->argv, io_flags)) {
600 report("Failed to open %s view", view->name);
601 return FALSE;
604 if (open_from_stdin(flags)) {
605 if (!io_open(&view->io, "%s", ""))
606 die("Failed to open stdin");
609 if (!extra)
610 setup_update(view, view->ops->id);
612 return TRUE;
615 bool
616 update_view(struct view *view)
618 char *line;
619 /* Clear the view and redraw everything since the tree sorting
620 * might have rearranged things. */
621 bool redraw = view->lines == 0;
622 bool can_read = TRUE;
623 struct encoding *encoding = view->encoding ? view->encoding : default_encoding;
625 if (!view->pipe)
626 return TRUE;
628 if (!io_can_read(view->pipe, FALSE)) {
629 if (view->lines == 0 && view_is_displayed(view)) {
630 time_t secs = time(NULL) - view->start_time;
632 if (secs > 1 && secs > view->update_secs) {
633 if (view->update_secs == 0)
634 redraw_view(view);
635 update_view_title(view);
636 view->update_secs = secs;
639 return TRUE;
642 for (; (line = io_get(view->pipe, '\n', can_read)); can_read = FALSE) {
643 if (encoding) {
644 line = encoding_convert(encoding, line);
647 if (!view->ops->read(view, line)) {
648 report("Allocation failure");
649 end_update(view, TRUE);
650 return FALSE;
654 if (io_error(view->pipe)) {
655 report("Failed to read: %s", io_strerror(view->pipe));
656 end_update(view, TRUE);
658 } else if (io_eof(view->pipe)) {
659 end_update(view, FALSE);
662 if (restore_view_position(view))
663 redraw = TRUE;
665 if (!view_is_displayed(view))
666 return TRUE;
668 if (redraw || view->force_redraw)
669 redraw_view_from(view, 0);
670 else
671 redraw_view_dirty(view);
672 view->force_redraw = FALSE;
674 /* Update the title _after_ the redraw so that if the redraw picks up a
675 * commit reference in view->ref it'll be available here. */
676 update_view_title(view);
677 return TRUE;
680 void
681 update_view_title(struct view *view)
683 WINDOW *window = view->title;
684 struct line *line = &view->line[view->pos.lineno];
685 unsigned int view_lines, lines;
687 assert(view_is_displayed(view));
689 if (view == display[current_view])
690 wbkgdset(window, get_view_attr(view, LINE_TITLE_FOCUS));
691 else
692 wbkgdset(window, get_view_attr(view, LINE_TITLE_BLUR));
694 werase(window);
695 mvwprintw(window, 0, 0, "[%s]", view->name);
697 if (*view->ref) {
698 wprintw(window, " %s", view->ref);
701 if (!view_has_flags(view, VIEW_CUSTOM_STATUS) && view_has_line(view, line) &&
702 line->lineno) {
703 wprintw(window, " - %s %d of %zd",
704 view->ops->type,
705 line->lineno,
706 view->lines - view->custom_lines);
709 if (view->pipe) {
710 time_t secs = time(NULL) - view->start_time;
712 /* Three git seconds are a long time ... */
713 if (secs > 2)
714 wprintw(window, " loading %lds", secs);
717 view_lines = view->pos.offset + view->height;
718 lines = view->lines ? MIN(view_lines, view->lines) * 100 / view->lines : 0;
719 mvwprintw(window, 0, view->width - count_digits(lines) - 1, "%d%%", lines);
721 wnoutrefresh(window);
725 * View opening
728 void
729 split_view(struct view *prev, struct view *view)
731 display[1] = view;
732 current_view = opt_focus_child ? 1 : 0;
733 view->parent = prev;
734 resize_display();
736 if (prev->pos.lineno - prev->pos.offset >= prev->height) {
737 /* Take the title line into account. */
738 int lines = prev->pos.lineno - prev->pos.offset - prev->height + 1;
740 /* Scroll the view that was split if the current line is
741 * outside the new limited view. */
742 do_scroll_view(prev, lines);
745 if (view != prev && view_is_displayed(prev)) {
746 /* "Blur" the previous view. */
747 update_view_title(prev);
751 void
752 maximize_view(struct view *view, bool redraw)
754 memset(display, 0, sizeof(display));
755 current_view = 0;
756 display[current_view] = view;
757 resize_display();
758 if (redraw) {
759 redraw_display(FALSE);
760 report_clear();
764 void
765 load_view(struct view *view, struct view *prev, enum open_flags flags)
767 bool refresh = !view_no_refresh(view, flags);
769 /* When prev == view it means this is the first loaded view. */
770 if (prev && view != prev) {
771 view->prev = prev;
774 if (refresh) {
775 if (view->pipe)
776 end_update(view, TRUE);
777 if (view->ops->private_size) {
778 if (!view->private) {
779 view->private = calloc(1, view->ops->private_size);
780 } else {
781 if (view->ops->done)
782 view->ops->done(view);
783 memset(view->private, 0, view->ops->private_size);
787 if (!view->ops->open(view, flags))
788 return;
791 if (prev) {
792 bool split = !!(flags & OPEN_SPLIT);
794 if (split) {
795 split_view(prev, view);
796 } else {
797 maximize_view(view, FALSE);
801 restore_view_position(view);
803 if (view->pipe && view->lines == 0) {
804 /* Clear the old view and let the incremental updating refill
805 * the screen. */
806 werase(view->win);
807 if (!(flags & (OPEN_RELOAD | OPEN_REFRESH)))
808 clear_position(&view->prev_pos);
809 report_clear();
810 } else if (view_is_displayed(view)) {
811 redraw_view(view);
812 report_clear();
816 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
817 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
819 void
820 open_view(struct view *prev, struct view *view, enum open_flags flags)
822 bool reload = !!(flags & (OPEN_RELOAD | OPEN_PREPARED));
823 int nviews = displayed_views();
825 assert(flags ^ OPEN_REFRESH);
827 if (view == prev && nviews == 1 && !reload) {
828 report("Already in %s view", view->name);
829 return;
832 if (!view_has_flags(view, VIEW_NO_GIT_DIR) && !repo.git_dir[0]) {
833 report("The %s view is disabled in pager mode", view->name);
834 return;
837 if (!view->keymap)
838 view->keymap = get_keymap(view->name, strlen(view->name));
839 load_view(view, prev ? prev : view, flags);
842 void
843 open_argv(struct view *prev, struct view *view, const char *argv[], const char *dir, enum open_flags flags)
845 if (view->pipe)
846 end_update(view, TRUE);
847 view->dir = dir;
849 if (!argv_copy(&view->argv, argv)) {
850 report("Failed to open %s view: %s", view->name, io_strerror(&view->io));
851 } else {
852 open_view(prev, view, flags | OPEN_PREPARED);
857 * Various utilities.
860 static struct view *sorting_view;
862 #define sort_order_reverse(state, result) \
863 ((state)->reverse ? -(result) : (result))
865 #define sort_order(state, cmp, o1, o2) \
866 sort_order_reverse(state, (!(o1) || !(o2)) ? !!(o2) - !!(o1) : cmp(o1, o2))
868 #define number_compare(size1, size2) (*(size1) - *(size2))
870 #define mode_is_dir(mode) ((mode) && S_ISDIR(*(mode)))
872 static int
873 sort_view_compare(const void *l1, const void *l2)
875 const struct line *line1 = l1;
876 const struct line *line2 = l2;
877 struct view_column_data column_data1 = {};
878 struct view_column_data column_data2 = {};
879 struct sort_state *sort = &sorting_view->sort;
881 if (!sorting_view->ops->get_column_data(sorting_view, line1, &column_data1))
882 return -1;
883 else if (!sorting_view->ops->get_column_data(sorting_view, line2, &column_data2))
884 return 1;
886 switch (get_sort_field(sorting_view)) {
887 case VIEW_COLUMN_AUTHOR:
888 return sort_order(sort, ident_compare, column_data1.author, column_data2.author);
890 case VIEW_COLUMN_DATE:
891 return sort_order(sort, timecmp, column_data1.date, column_data2.date);
893 case VIEW_COLUMN_ID:
894 if (column_data1.reflog && column_data2.reflog)
895 return sort_order(sort, strcmp, column_data1.reflog, column_data2.reflog);
896 return sort_order(sort, strcmp, column_data1.id, column_data2.id);
898 case VIEW_COLUMN_FILE_NAME:
899 if (mode_is_dir(column_data1.mode) != mode_is_dir(column_data2.mode))
900 return sort_order_reverse(sort, mode_is_dir(column_data1.mode) ? -1 : 1);
901 return sort_order(sort, strcmp, column_data1.file_name, column_data2.file_name);
903 case VIEW_COLUMN_FILE_SIZE:
904 return sort_order(sort, number_compare, column_data1.file_size, column_data2.file_size);
906 case VIEW_COLUMN_LINE_NUMBER:
907 return sort_order_reverse(sort, line1->lineno - line2->lineno);
909 case VIEW_COLUMN_MODE:
910 return sort_order(sort, number_compare, column_data1.mode, column_data2.mode);
912 case VIEW_COLUMN_REF:
913 return sort_order(sort, ref_compare, column_data1.ref, column_data2.ref);
915 case VIEW_COLUMN_COMMIT_TITLE:
916 return sort_order(sort, strcmp, column_data1.commit_title, column_data2.commit_title);
918 case VIEW_COLUMN_SECTION:
919 return sort_order(sort, strcmp, column_data1.section->opt.section.text,
920 column_data2.section->opt.section.text);
922 case VIEW_COLUMN_STATUS:
923 return sort_order(sort, number_compare, column_data1.status, column_data2.status);
925 case VIEW_COLUMN_TEXT:
926 return sort_order(sort, strcmp, column_data1.text, column_data2.text);
929 return 0;
932 void
933 sort_view(struct view *view, bool change_field)
935 struct sort_state *state = &view->sort;
937 if (change_field) {
938 while (TRUE) {
939 state->current = state->current->next
940 ? state->current->next : view->columns;
941 if (get_sort_field(view) == VIEW_COLUMN_ID &&
942 !state->current->opt.id.show)
943 continue;
944 break;
946 } else {
947 state->reverse = !state->reverse;
950 sorting_view = view;
951 qsort(view->line, view->lines, sizeof(*view->line), sort_view_compare);
954 static const char *
955 view_column_text(struct view *view, struct view_column_data *column_data,
956 struct view_column *column)
958 const char *text = "";
960 switch (column->type) {
961 case VIEW_COLUMN_AUTHOR:
962 if (column_data->author)
963 text = mkauthor(column_data->author, column->opt.author.width, column->opt.author.show);
964 break;
966 case VIEW_COLUMN_COMMIT_TITLE:
967 text = column_data->commit_title;
968 break;
970 case VIEW_COLUMN_DATE:
971 if (column_data->date)
972 text = mkdate(column_data->date, column->opt.date.show);
973 break;
975 case VIEW_COLUMN_REF:
976 if (column_data->ref)
977 text = column_data->ref->name;
978 break;
980 case VIEW_COLUMN_FILE_NAME:
981 if (column_data->file_name)
982 text = column_data->file_name;
983 break;
985 case VIEW_COLUMN_FILE_SIZE:
986 if (column_data->file_size)
987 text = mkfilesize(*column_data->file_size, column->opt.file_size.show);
988 break;
990 case VIEW_COLUMN_ID:
991 if (column->opt.id.show)
992 text = column_data->reflog ? column_data->reflog : column_data->id;
993 break;
995 case VIEW_COLUMN_LINE_NUMBER:
996 break;
998 case VIEW_COLUMN_MODE:
999 if (column_data->mode)
1000 text = mkmode(*column_data->mode);
1001 break;
1003 case VIEW_COLUMN_STATUS:
1004 if (column_data->status) {
1005 static char buf[] = "?";
1007 buf[0] = *column_data->status;
1008 text = buf;
1010 break;
1012 case VIEW_COLUMN_SECTION:
1013 text = column_data->section->opt.section.text;
1014 break;
1016 case VIEW_COLUMN_TEXT:
1017 text = column_data->text;
1018 break;
1021 return text ? text : "";
1024 static bool
1025 grep_refs(struct view *view, struct view_column *column, const struct ref_list *list)
1027 regmatch_t pmatch;
1028 size_t i;
1030 if (!list)
1031 return FALSE;
1033 for (i = 0; i < list->size; i++) {
1034 if (!regexec(view->regex, list->refs[i]->name, 1, &pmatch, 0))
1035 return TRUE;
1038 return FALSE;
1041 bool
1042 view_column_grep(struct view *view, struct line *line)
1044 struct view_column_data column_data = {};
1045 bool ok = view->ops->get_column_data(view, line, &column_data);
1046 struct view_column *column;
1048 if (!ok)
1049 return FALSE;
1051 for (column = view->columns; column; column = column->next) {
1052 const char *text[] = {
1053 view_column_text(view, &column_data, column),
1054 NULL
1057 if (grep_text(view, text))
1058 return TRUE;
1060 if (column->type == VIEW_COLUMN_COMMIT_TITLE &&
1061 column->opt.commit_title.refs &&
1062 grep_refs(view, column, column_data.refs))
1063 return TRUE;
1066 return FALSE;
1069 bool
1070 view_column_info_changed(struct view *view, bool update)
1072 struct view_column *column;
1073 bool changed = FALSE;
1075 for (column = view->columns; column; column = column->next) {
1076 if (memcmp(&column->prev_opt, &column->opt, sizeof(column->opt))) {
1077 if (!update)
1078 return TRUE;
1079 column->prev_opt = column->opt;
1080 changed = TRUE;
1084 return changed;
1087 void
1088 view_column_reset(struct view *view)
1090 struct view_column *column;
1092 view_column_info_changed(view, TRUE);
1093 for (column = view->columns; column; column = column->next)
1094 column->width = 0;
1097 static enum status_code
1098 parse_view_column_config(char **pos, const char **name, const char **value, bool first)
1100 size_t len = strcspn(*pos, ",");
1101 size_t optlen;
1103 if (strlen(*pos) > len)
1104 (*pos)[len] = 0;
1105 optlen = strcspn(*pos, ":=");
1107 if (first) {
1108 *name = "show";
1110 if (optlen == len) {
1111 *value = len ? *pos : "yes";
1112 *pos += len + 1;
1113 return SUCCESS;
1116 /* Fake boolean enum value. */
1117 *value = "yes";
1118 return SUCCESS;
1121 *name = *pos;
1122 if (optlen == len)
1123 *value = "yes";
1124 else
1125 *value = *pos + optlen + 1;
1126 (*pos)[optlen] = 0;
1127 *pos += len + 1;
1129 return SUCCESS;
1132 static enum status_code
1133 parse_view_column_option(struct view_column *column,
1134 const char *opt_name, const char *opt_value)
1136 #define DEFINE_COLUMN_OPTION_INFO(name, type, flags) \
1137 { #name, STRING_SIZE(#name), #type, &opt->name },
1139 #define DEFINE_COLUMN_OPTIONS_PARSE(name, id, options) \
1140 if (column->type == VIEW_COLUMN_##id) { \
1141 struct name##_options *opt = &column->opt.name; \
1142 struct option_info info[] = { \
1143 options(DEFINE_COLUMN_OPTION_INFO) \
1144 }; \
1145 struct option_info *option = find_option_info(info, ARRAY_SIZE(info), opt_name); \
1146 if (!option) \
1147 return error("Unknown option `%s' for column %s", opt_name, \
1148 view_column_name(VIEW_COLUMN_##id)); \
1149 return parse_option(option, #name, opt_value); \
1152 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_PARSE);
1154 return error("Unknown view column option: %s", opt_name);
1157 static enum status_code
1158 parse_view_column_type(struct view_column *column, const char **arg)
1160 enum view_column_type type;
1161 size_t typelen = strcspn(*arg, ":,");
1163 for (type = 0; type < view_column_type_map->size; type++)
1164 if (enum_equals(view_column_type_map->entries[type], *arg, typelen)) {
1165 *arg += typelen + !!(*arg)[typelen];
1166 column->type = type;
1167 return SUCCESS;
1170 return error("Failed to parse view column type: %.*s", (int) typelen, *arg);
1173 static struct view *
1174 find_view(const char *view_name)
1176 struct view *view;
1177 int i;
1179 foreach_view(view, i)
1180 if (!strncmp(view_name, view->name, strlen(view->name)))
1181 return view;
1183 return NULL;
1186 enum status_code
1187 parse_view_config(const char *view_name, const char *argv[])
1189 enum status_code code = SUCCESS;
1190 size_t size = argv_size(argv);
1191 struct view_column *columns;
1192 struct view_column *column;
1193 struct view *view = find_view(view_name);
1194 int i;
1196 if (!view)
1197 return error("Unknown view: %s", view_name);
1199 columns = calloc(size, sizeof(*columns));
1200 if (!columns)
1201 return ERROR_OUT_OF_MEMORY;
1203 for (i = 0, column = NULL; code == SUCCESS && i < size; i++) {
1204 const char *arg = argv[i];
1205 char buf[SIZEOF_STR] = "";
1206 char *pos, *end;
1208 if (column)
1209 column->next = &columns[i];
1210 column = &columns[i];
1212 code = parse_view_column_type(column, &arg);
1213 if (code != SUCCESS)
1214 break;
1216 if (!(view->ops->column_bits & (1 << column->type)))
1217 return error("The %s view does not support %s column", view->name,
1218 view_column_name(column->type));
1220 if ((column->type == VIEW_COLUMN_TEXT ||
1221 column->type == VIEW_COLUMN_COMMIT_TITLE) &&
1222 i + 1 < size)
1223 return error("The %s column must always be last",
1224 view_column_name(column->type));
1226 string_ncopy(buf, arg, strlen(arg));
1228 for (pos = buf, end = pos + strlen(pos); code == SUCCESS && pos <= end; ) {
1229 const char *name = NULL;
1230 const char *value = NULL;
1232 code = parse_view_column_config(&pos, &name, &value, buf == pos);
1233 if (code == SUCCESS)
1234 code = parse_view_column_option(column, name, value);
1237 column->prev_opt = column->opt;
1240 if (code == SUCCESS) {
1241 free(view->columns);
1242 view->columns = columns;
1243 view->sort.current = view->columns;
1244 } else {
1245 free(columns);
1248 return code;
1251 struct view_column *
1252 get_view_column(struct view *view, enum view_column_type type)
1254 struct view_column *column;
1256 for (column = view->columns; column; column = column->next)
1257 if (column->type == type)
1258 return column;
1259 return NULL;
1262 bool
1263 view_column_info_update(struct view *view, struct line *line)
1265 struct view_column_data column_data = {};
1266 struct view_column *column;
1267 bool changed = FALSE;
1269 if (!view->ops->get_column_data(view, line, &column_data))
1270 return FALSE;
1272 for (column = view->columns; column; column = column->next) {
1273 const char *text = view_column_text(view, &column_data, column);
1274 int width = 0;
1276 switch (column->type) {
1277 case VIEW_COLUMN_AUTHOR:
1278 width = column->opt.author.width;
1279 break;
1281 case VIEW_COLUMN_COMMIT_TITLE:
1282 width = column->opt.commit_title.width;
1283 break;
1285 case VIEW_COLUMN_DATE:
1286 width = column->opt.date.width;
1287 break;
1289 case VIEW_COLUMN_FILE_NAME:
1290 width = column->opt.file_name.width;
1291 break;
1293 case VIEW_COLUMN_FILE_SIZE:
1294 width = column->opt.file_size.width;
1295 break;
1297 case VIEW_COLUMN_ID:
1298 width = column->opt.id.width;
1299 if (!width)
1300 width = opt_id_width;
1301 if (!column_data.reflog && !width)
1302 width = 7;
1303 break;
1305 case VIEW_COLUMN_LINE_NUMBER:
1306 if (column_data.line_number)
1307 width = count_digits(*column_data.line_number);
1308 else
1309 width = count_digits(view->lines);
1310 if (width < 3)
1311 width = 3;
1312 break;
1314 case VIEW_COLUMN_MODE:
1315 width = column->opt.mode.width;
1316 break;
1318 case VIEW_COLUMN_REF:
1319 width = column->opt.ref.width;
1320 break;
1322 case VIEW_COLUMN_SECTION:
1323 break;
1325 case VIEW_COLUMN_STATUS:
1326 width = column->opt.status.width;
1327 break;
1329 case VIEW_COLUMN_TEXT:
1330 width = column->opt.text.width;
1331 break;
1334 if (*text && !width)
1335 width = utf8_width(text);
1337 if (width > column->width) {
1338 column->width = width;
1339 changed = TRUE;
1343 if (changed)
1344 view->force_redraw = TRUE;
1345 return changed;
1348 struct line *
1349 find_line_by_type(struct view *view, struct line *line, enum line_type type, int direction)
1351 for (; view_has_line(view, line); line += direction)
1352 if (line->type == type)
1353 return line;
1355 return NULL;
1359 * Line utilities.
1362 DEFINE_ALLOCATOR(realloc_lines, struct line, 256)
1364 struct line *
1365 add_line_at(struct view *view, unsigned long pos, const void *data, enum line_type type, size_t data_size, bool custom)
1367 struct line *line;
1368 unsigned long lineno;
1370 if (!realloc_lines(&view->line, view->lines, 1))
1371 return NULL;
1373 if (data_size) {
1374 void *alloc_data = calloc(1, data_size);
1376 if (!alloc_data)
1377 return NULL;
1379 if (data)
1380 memcpy(alloc_data, data, data_size);
1381 data = alloc_data;
1384 if (pos < view->lines) {
1385 view->lines++;
1386 line = view->line + pos;
1387 lineno = line->lineno;
1389 memmove(line + 1, line, (view->lines - pos) * sizeof(*view->line));
1390 while (pos < view->lines) {
1391 view->line[pos].lineno++;
1392 view->line[pos++].dirty = 1;
1394 } else {
1395 line = &view->line[view->lines++];
1396 lineno = view->lines - view->custom_lines;
1399 memset(line, 0, sizeof(*line));
1400 line->type = type;
1401 line->data = (void *) data;
1402 line->dirty = 1;
1404 if (custom)
1405 view->custom_lines++;
1406 else
1407 line->lineno = lineno;
1409 return line;
1412 struct line *
1413 add_line(struct view *view, const void *data, enum line_type type, size_t data_size, bool custom)
1415 return add_line_at(view, view->lines, data, type, data_size, custom);
1418 struct line *
1419 add_line_alloc_(struct view *view, void **ptr, enum line_type type, size_t data_size, bool custom)
1421 struct line *line = add_line(view, NULL, type, data_size, custom);
1423 if (line)
1424 *ptr = line->data;
1425 return line;
1428 struct line *
1429 add_line_nodata(struct view *view, enum line_type type)
1431 return add_line(view, NULL, type, 0, FALSE);
1434 struct line *
1435 add_line_text(struct view *view, const char *text, enum line_type type)
1437 struct line *line = add_line(view, text, type, strlen(text) + 1, FALSE);
1439 if (line && view->ops->column_bits)
1440 view_column_info_update(view, line);
1441 return line;
1444 struct line * PRINTF_LIKE(3, 4)
1445 add_line_format(struct view *view, enum line_type type, const char *fmt, ...)
1447 char buf[SIZEOF_STR];
1448 int retval;
1450 FORMAT_BUFFER(buf, sizeof(buf), fmt, retval, FALSE);
1451 return retval >= 0 ? add_line_text(view, buf, type) : NULL;
1455 * Global view state.
1458 /* Included last to not pollute the rest of the file. */
1459 #include "tig/main.h"
1460 #include "tig/diff.h"
1461 #include "tig/log.h"
1462 #include "tig/tree.h"
1463 #include "tig/blob.h"
1464 #include "tig/blame.h"
1465 #include "tig/refs.h"
1466 #include "tig/status.h"
1467 #include "tig/stage.h"
1468 #include "tig/stash.h"
1469 #include "tig/grep.h"
1470 #include "tig/pager.h"
1471 #include "tig/help.h"
1473 static struct view *views[] = {
1474 #define VIEW_DATA(id, name) &name##_view
1475 VIEW_INFO(VIEW_DATA)
1478 struct view *
1479 get_view(int i)
1481 return 0 <= i && i < ARRAY_SIZE(views) ? views[i] : NULL;
1484 /* vim: set ts=8 sw=8 noexpandtab: */