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.
17 #include "tig/options.h"
20 #include "tig/display.h"
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;
36 offset
= lineno
- half
;
41 if (offset
!= view
->pos
.offset
|| lineno
!= view
->pos
.lineno
) {
42 view
->pos
.offset
= offset
;
43 view
->pos
.lineno
= lineno
;
50 /* Scrolling backend */
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
);
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
)) {
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
))
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
;
99 scroll_view(struct view
*view
, enum request request
)
103 assert(view_is_displayed(view
));
105 if (request
== REQ_SCROLL_WHEEL_DOWN
|| request
== REQ_SCROLL_WHEEL_UP
)
106 lines
= opt_mouse_scroll
;
109 case REQ_SCROLL_FIRST_COL
:
111 redraw_view_from(view
, 0);
114 case REQ_SCROLL_LEFT
:
115 if (view
->pos
.col
== 0) {
116 report("Cannot scroll beyond the first column");
119 if (view
->pos
.col
<= apply_step(opt_horizontal_scroll
, view
->width
))
122 view
->pos
.col
-= apply_step(opt_horizontal_scroll
, view
->width
);
123 redraw_view_from(view
, 0);
126 case REQ_SCROLL_RIGHT
:
127 view
->pos
.col
+= apply_step(opt_horizontal_scroll
, view
->width
);
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");
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
;
152 report("Cannot scroll beyond the first line");
160 die("request %d not handled in switch", request
);
163 do_scroll_view(view
, lines
);
168 move_view(struct view
*view
, enum request request
)
170 int scroll_steps
= 0;
174 case REQ_MOVE_FIRST_LINE
:
175 steps
= -view
->pos
.lineno
;
178 case REQ_MOVE_LAST_LINE
:
179 steps
= view
->lines
- view
->pos
.lineno
- 1;
182 case REQ_MOVE_PAGE_UP
:
183 steps
= view
->height
> view
->pos
.lineno
184 ? -view
->pos
.lineno
: -view
->height
;
187 case REQ_MOVE_PAGE_DOWN
:
188 steps
= view
->pos
.lineno
+ view
->height
>= view
->lines
189 ? view
->lines
- view
->pos
.lineno
- 1 : view
->height
;
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");
210 } else if (steps
>= 0 && view
->pos
.lineno
+ 1 >= view
->lines
) {
211 report("Cannot move beyond the last line");
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
]);
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
);
248 do_scroll_view(view
, scroll_steps
);
252 /* Draw the current line */
253 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
255 wnoutrefresh(view
->win
);
263 DEFINE_ALLOCATOR(realloc_unsigned_ints
, unsigned int, 32)
266 grep_text(struct view
*view
, const char *text
[])
271 for (i
= 0; text
[i
]; i
++)
272 if (*text
[i
] && !regexec(view
->regex
, text
[i
], 1, &pmatch
, 0))
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
) {
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
);
292 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
298 find_matches(struct view
*view
)
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
]))
308 if (!realloc_unsigned_ints(&view
->matched_line
, view
->matched_lines
, 1))
311 view
->matched_line
[view
->matched_lines
++] = lineno
;
318 find_next(struct view
*view
, enum request request
)
324 if (!*view
->env
->search
)
325 report("No previous search");
327 search_view(view
, request
);
337 case REQ_SEARCH_BACK
:
346 if (!view
->matched_lines
&& !find_matches(view
)) {
347 report("Allocation failure");
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
)
360 if (direction
< 0 && lineno
>= view
->pos
.lineno
)
363 select_view_line(view
, lineno
);
364 report("Line %zu matches '%s' (%zu of %zu)", lineno
+ 1, view
->grep
, i
+ 1, view
->matched_lines
);
368 report("No match found for '%s'", view
->grep
);
372 reset_matches(struct view
*view
)
374 free(view
->matched_line
);
375 view
->matched_line
= NULL
;
376 view
->matched_lines
= 0;
380 search_view(struct view
*view
, enum request request
)
383 int regex_flags
= opt_ignore_case
? REG_ICASE
: 0;
386 regfree(view
->regex
);
389 view
->regex
= calloc(1, sizeof(*view
->regex
));
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
);
403 string_copy(view
->grep
, view
->env
->search
);
407 find_next(view
, request
);
415 view_history_is_empty(struct view_history
*history
)
417 return !history
->stack
;
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
))
429 state
= calloc(1, sizeof(*state
) + history
->state_alloc
);
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
);
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
))
451 history
->position
= state
->position
;
452 history
->stack
= state
->prev
;
454 if (data
&& history
->state_alloc
)
455 memcpy(data
, state
->data
, history
->state_alloc
);
457 *position
= state
->position
;
464 reset_view_history(struct view_history
*history
)
466 while (pop_view_history_state(history
, NULL
, NULL
))
471 * Incremental updating
475 reset_view(struct view
*view
)
479 for (i
= 0; i
< view
->lines
; i
++)
480 free(view
->line
[i
].data
);
484 view
->prev_pos
= view
->pos
;
485 clear_position(&view
->pos
);
488 view_column_reset(view
);
493 view
->custom_lines
= 0;
494 view
->update_secs
= 0;
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
);
518 if (goto_view_line(view
, view
->prev_pos
.offset
, view
->prev_pos
.lineno
) &&
519 view_is_displayed(view
))
522 view
->pos
.col
= view
->prev_pos
.col
;
523 clear_position(&view
->prev_pos
);
529 end_update(struct view
*view
, bool force
)
533 while (!view
->ops
->read(view
, NULL
))
543 setup_update(struct view
*view
, const char *vid
)
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
);
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
);
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
))
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
;
586 if (!argv_format(view
->env
, &view
->argv
, argv
, !view
->prev
, file_filter
)) {
587 report("Failed to format %s arguments", view
->name
);
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
);
604 if (open_from_stdin(flags
)) {
605 if (!io_open(&view
->io
, "%s", ""))
606 die("Failed to open stdin");
610 setup_update(view
, view
->ops
->id
);
616 update_view(struct view
*view
)
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
;
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)
635 update_view_title(view
);
636 view
->update_secs
= secs
;
642 for (; (line
= io_get(view
->pipe
, '\n', can_read
)); can_read
= FALSE
) {
644 line
= encoding_convert(encoding
, line
);
647 if (!view
->ops
->read(view
, line
)) {
648 report("Allocation failure");
649 end_update(view
, TRUE
);
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
))
665 if (!view_is_displayed(view
))
668 if (redraw
|| view
->force_redraw
)
669 redraw_view_from(view
, 0);
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
);
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
));
692 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_BLUR
));
695 mvwprintw(window
, 0, 0, "[%s]", view
->name
);
698 wprintw(window
, " %s", view
->ref
);
701 if (!view_has_flags(view
, VIEW_CUSTOM_STATUS
) && view_has_line(view
, line
) &&
703 wprintw(window
, " - %s %d of %zd",
706 view
->lines
- view
->custom_lines
);
710 time_t secs
= time(NULL
) - view
->start_time
;
712 /* Three git seconds are a long time ... */
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
);
729 split_view(struct view
*prev
, struct view
*view
)
732 current_view
= opt_focus_child
? 1 : 0;
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
);
752 maximize_view(struct view
*view
, bool redraw
)
754 memset(display
, 0, sizeof(display
));
756 display
[current_view
] = view
;
759 redraw_display(FALSE
);
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
) {
776 end_update(view
, TRUE
);
777 if (view
->ops
->private_size
) {
778 if (!view
->private) {
779 view
->private = calloc(1, view
->ops
->private_size
);
782 view
->ops
->done(view
);
783 memset(view
->private, 0, view
->ops
->private_size
);
787 if (!view
->ops
->open(view
, flags
))
792 bool split
= !!(flags
& OPEN_SPLIT
);
795 split_view(prev
, view
);
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
807 if (!(flags
& (OPEN_RELOAD
| OPEN_REFRESH
)))
808 clear_position(&view
->prev_pos
);
810 } else if (view_is_displayed(view
)) {
816 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
817 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
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
);
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
);
838 view
->keymap
= get_keymap(view
->name
, strlen(view
->name
));
839 load_view(view
, prev
? prev
: view
, flags
);
843 open_argv(struct view
*prev
, struct view
*view
, const char *argv
[], const char *dir
, enum open_flags flags
)
846 end_update(view
, TRUE
);
849 if (!argv_copy(&view
->argv
, argv
)) {
850 report("Failed to open %s view: %s", view
->name
, io_strerror(&view
->io
));
852 open_view(prev
, view
, flags
| OPEN_PREPARED
);
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)))
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
))
883 else if (!sorting_view
->ops
->get_column_data(sorting_view
, line2
, &column_data2
))
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
);
894 return sort_order(sort
, strcmp
, column_data1
.id
, column_data2
.id
);
896 case VIEW_COLUMN_FILE_NAME
:
897 if (mode_is_dir(column_data1
.mode
) != mode_is_dir(column_data2
.mode
))
898 return sort_order_reverse(sort
, mode_is_dir(column_data1
.mode
) ? -1 : 1);
899 return sort_order(sort
, strcmp
, column_data1
.file_name
, column_data2
.file_name
);
901 case VIEW_COLUMN_FILE_SIZE
:
902 return sort_order(sort
, number_compare
, column_data1
.file_size
, column_data2
.file_size
);
904 case VIEW_COLUMN_LINE_NUMBER
:
905 return sort_order_reverse(sort
, line1
->lineno
- line2
->lineno
);
907 case VIEW_COLUMN_MODE
:
908 return sort_order(sort
, number_compare
, column_data1
.mode
, column_data2
.mode
);
910 case VIEW_COLUMN_REF
:
911 return sort_order(sort
, ref_compare
, column_data1
.ref
, column_data2
.ref
);
913 case VIEW_COLUMN_COMMIT_TITLE
:
914 return sort_order(sort
, strcmp
, column_data1
.commit_title
, column_data2
.commit_title
);
916 case VIEW_COLUMN_TEXT
:
917 return sort_order(sort
, strcmp
, column_data1
.text
, column_data2
.text
);
925 sort_view(struct view
*view
, bool change_field
)
927 struct sort_state
*state
= &view
->sort
;
931 state
->current
= state
->current
->next
932 ? state
->current
->next
: view
->columns
;
933 if (get_sort_field(view
) == VIEW_COLUMN_ID
&&
934 !state
->current
->opt
.id
.show
)
939 state
->reverse
= !state
->reverse
;
943 qsort(view
->line
, view
->lines
, sizeof(*view
->line
), sort_view_compare
);
947 view_column_text(struct view
*view
, struct view_column_data
*column_data
,
948 struct view_column
*column
)
950 const char *text
= "";
952 switch (column
->type
) {
953 case VIEW_COLUMN_AUTHOR
:
954 if (column_data
->author
)
955 text
= mkauthor(column_data
->author
, column
->opt
.author
.width
, column
->opt
.author
.show
);
958 case VIEW_COLUMN_COMMIT_TITLE
:
959 text
= column_data
->commit_title
;
962 case VIEW_COLUMN_DATE
:
963 if (column_data
->date
)
964 text
= mkdate(column_data
->date
, column
->opt
.date
.show
);
967 case VIEW_COLUMN_REF
:
968 if (column_data
->ref
)
969 text
= column_data
->ref
->name
;
972 case VIEW_COLUMN_FILE_NAME
:
973 if (column_data
->file_name
)
974 text
= column_data
->file_name
;
977 case VIEW_COLUMN_FILE_SIZE
:
978 if (column_data
->file_size
)
979 text
= mkfilesize(*column_data
->file_size
, column
->opt
.file_size
.show
);
983 if (column
->opt
.id
.show
)
984 text
= column_data
->id
;
987 case VIEW_COLUMN_LINE_NUMBER
:
990 case VIEW_COLUMN_MODE
:
991 if (column_data
->mode
)
992 text
= mkmode(*column_data
->mode
);
995 case VIEW_COLUMN_TEXT
:
996 text
= column_data
->text
;
1000 return text
? text
: "";
1004 grep_refs(struct view
*view
, const struct ref_list
*list
)
1009 if (!opt_show_refs
|| !list
)
1012 for (i
= 0; i
< list
->size
; i
++) {
1013 if (!regexec(view
->regex
, list
->refs
[i
]->name
, 1, &pmatch
, 0))
1021 view_column_grep(struct view
*view
, struct line
*line
)
1023 struct view_column_data column_data
= {};
1024 bool ok
= view
->ops
->get_column_data(view
, line
, &column_data
);
1025 struct view_column
*column
;
1030 for (column
= view
->columns
; column
; column
= column
->next
) {
1031 const char *text
[] = {
1032 view_column_text(view
, &column_data
, column
),
1036 if (grep_text(view
, text
))
1040 return grep_refs(view
, column_data
.refs
);
1044 view_column_info_changed(struct view
*view
, bool update
)
1046 struct view_column
*column
;
1047 bool changed
= FALSE
;
1049 for (column
= view
->columns
; column
; column
= column
->next
) {
1050 if (memcmp(&column
->prev_opt
, &column
->opt
, sizeof(column
->opt
))) {
1053 column
->prev_opt
= column
->opt
;
1062 view_column_reset(struct view
*view
)
1064 struct view_column
*column
;
1066 view_column_info_changed(view
, TRUE
);
1067 for (column
= view
->columns
; column
; column
= column
->next
)
1072 view_column_init(struct view
*view
, const enum view_column_type columns
[], size_t columns_size
)
1074 struct view_column
*column
;
1080 view
->columns
= calloc(columns_size
, sizeof(*view
->columns
));
1084 view
->sort
.current
= view
->columns
;
1085 for (column
= NULL
, i
= 0; i
< columns_size
; i
++) {
1086 union view_column_options opt
= {};
1089 column
->next
= &view
->columns
[i
];
1090 column
= &view
->columns
[i
];
1091 column
->type
= columns
[i
];
1093 switch (column
->type
) {
1094 case VIEW_COLUMN_AUTHOR
:
1095 opt
.author
.show
= opt_show_author
;
1096 opt
.author
.width
= opt_author_width
;
1099 case VIEW_COLUMN_COMMIT_TITLE
:
1100 opt
.commit_title
.overflow
= opt_title_overflow
;
1101 opt
.commit_title
.refs
= opt_show_refs
;
1102 opt
.commit_title
.graph
= opt_show_rev_graph
;
1105 case VIEW_COLUMN_DATE
:
1106 opt
.date
.show
= opt_show_date
;
1109 case VIEW_COLUMN_REF
:
1112 case VIEW_COLUMN_FILE_NAME
:
1113 opt
.file_name
.show
= opt_show_filename
;
1114 opt
.file_name
.width
= opt_show_filename_width
;
1117 case VIEW_COLUMN_FILE_SIZE
:
1118 opt
.file_size
.show
= opt_show_file_size
;
1121 case VIEW_COLUMN_ID
:
1122 opt
.id
.show
= opt_show_id
;
1123 opt
.id
.width
= opt_id_width
;
1126 case VIEW_COLUMN_LINE_NUMBER
:
1127 opt
.line_number
.show
= opt_show_line_numbers
;
1128 opt
.line_number
.interval
= opt_line_number_interval
;
1131 case VIEW_COLUMN_MODE
:
1134 case VIEW_COLUMN_TEXT
:
1135 opt
.text
.commit_title_overflow
= opt_title_overflow
;
1140 column
->prev_opt
= opt
;
1146 struct view_column
*
1147 get_view_column(struct view
*view
, enum view_column_type type
)
1149 struct view_column
*column
;
1151 for (column
= view
->columns
; column
; column
= column
->next
)
1152 if (column
->type
== type
)
1158 view_column_info_update(struct view
*view
, struct line
*line
)
1160 struct view_column_data column_data
= {};
1161 struct view_column
*column
;
1162 bool changed
= FALSE
;
1164 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
1167 for (column
= view
->columns
; column
; column
= column
->next
) {
1168 const char *text
= view_column_text(view
, &column_data
, column
);
1171 switch (column
->type
) {
1172 case VIEW_COLUMN_AUTHOR
:
1173 width
= column
->opt
.author
.width
;
1176 case VIEW_COLUMN_DATE
:
1177 width
= column
->opt
.date
.width
;
1180 case VIEW_COLUMN_REF
:
1181 width
= column
->opt
.ref
.width
;
1184 case VIEW_COLUMN_FILE_NAME
:
1185 width
= column
->opt
.file_name
.width
;
1188 case VIEW_COLUMN_FILE_SIZE
:
1189 width
= column
->opt
.file_size
.width
;
1192 case VIEW_COLUMN_ID
:
1193 width
= column
->opt
.id
.width
;
1194 if (iscommit(text
) && !width
)
1198 case VIEW_COLUMN_LINE_NUMBER
:
1199 if (column_data
.line_number
)
1200 width
= count_digits(*column_data
.line_number
);
1202 width
= count_digits(view
->lines
);
1207 case VIEW_COLUMN_COMMIT_TITLE
:
1208 case VIEW_COLUMN_MODE
:
1209 case VIEW_COLUMN_TEXT
:
1213 if (*text
&& !width
)
1214 width
= utf8_width(text
);
1216 if (width
> column
->width
) {
1217 column
->width
= width
;
1223 view
->force_redraw
= TRUE
;
1228 find_line_by_type(struct view
*view
, struct line
*line
, enum line_type type
, int direction
)
1230 for (; view_has_line(view
, line
); line
+= direction
)
1231 if (line
->type
== type
)
1241 DEFINE_ALLOCATOR(realloc_lines
, struct line
, 256)
1244 add_line_at(struct view
*view
, unsigned long pos
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1247 unsigned long lineno
;
1249 if (!realloc_lines(&view
->line
, view
->lines
, 1))
1253 void *alloc_data
= calloc(1, data_size
);
1259 memcpy(alloc_data
, data
, data_size
);
1263 if (pos
< view
->lines
) {
1265 line
= view
->line
+ pos
;
1266 lineno
= line
->lineno
;
1268 memmove(line
+ 1, line
, (view
->lines
- pos
) * sizeof(*view
->line
));
1269 while (pos
< view
->lines
) {
1270 view
->line
[pos
].lineno
++;
1271 view
->line
[pos
++].dirty
= 1;
1274 line
= &view
->line
[view
->lines
++];
1275 lineno
= view
->lines
- view
->custom_lines
;
1278 memset(line
, 0, sizeof(*line
));
1280 line
->data
= (void *) data
;
1284 view
->custom_lines
++;
1286 line
->lineno
= lineno
;
1292 add_line(struct view
*view
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1294 return add_line_at(view
, view
->lines
, data
, type
, data_size
, custom
);
1298 add_line_alloc_(struct view
*view
, void **ptr
, enum line_type type
, size_t data_size
, bool custom
)
1300 struct line
*line
= add_line(view
, NULL
, type
, data_size
, custom
);
1308 add_line_nodata(struct view
*view
, enum line_type type
)
1310 return add_line(view
, NULL
, type
, 0, FALSE
);
1314 add_line_text(struct view
*view
, const char *text
, enum line_type type
)
1316 return add_line(view
, text
, type
, strlen(text
) + 1, FALSE
);
1319 struct line
* PRINTF_LIKE(3, 4)
1320 add_line_format(struct view
*view
, enum line_type type
, const char *fmt
, ...)
1322 char buf
[SIZEOF_STR
];
1325 FORMAT_BUFFER(buf
, sizeof(buf
), fmt
, retval
, FALSE
);
1326 return retval
>= 0 ? add_line_text(view
, buf
, type
) : NULL
;
1330 * Global view state.
1333 /* Included last to not pollute the rest of the file. */
1334 #include "tig/main.h"
1335 #include "tig/diff.h"
1336 #include "tig/log.h"
1337 #include "tig/tree.h"
1338 #include "tig/blob.h"
1339 #include "tig/blame.h"
1340 #include "tig/refs.h"
1341 #include "tig/status.h"
1342 #include "tig/stage.h"
1343 #include "tig/stash.h"
1344 #include "tig/grep.h"
1345 #include "tig/pager.h"
1346 #include "tig/help.h"
1348 static struct view
*views
[] = {
1349 #define VIEW_DATA(id, name) &name##_view
1350 VIEW_INFO(VIEW_DATA
)
1356 return 0 <= i
&& i
< ARRAY_SIZE(views
) ? views
[i
] : NULL
;
1359 /* vim: set ts=8 sw=8 noexpandtab: */