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
);
264 grep_text(struct view
*view
, const char *text
[])
269 for (i
= 0; text
[i
]; i
++)
270 if (*text
[i
] && !regexec(view
->regex
, text
[i
], 1, &pmatch
, 0))
276 select_view_line(struct view
*view
, unsigned long lineno
)
278 struct position old
= view
->pos
;
280 if (goto_view_line(view
, view
->pos
.offset
, lineno
)) {
281 if (view_is_displayed(view
)) {
282 if (old
.offset
!= view
->pos
.offset
) {
285 draw_view_line(view
, old
.lineno
- view
->pos
.offset
);
286 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
287 wnoutrefresh(view
->win
);
290 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
296 find_next(struct view
*view
, enum request request
)
298 unsigned long lineno
= view
->pos
.lineno
;
302 if (!*view
->env
->search
)
303 report("No previous search");
305 search_view(view
, request
);
315 case REQ_SEARCH_BACK
:
324 if (request
== REQ_FIND_NEXT
|| request
== REQ_FIND_PREV
)
327 /* Note, lineno is unsigned long so will wrap around in which case it
328 * will become bigger than view->lines. */
329 for (; lineno
< view
->lines
; lineno
+= direction
) {
330 if (view
->ops
->grep(view
, &view
->line
[lineno
])) {
331 select_view_line(view
, lineno
);
332 report("Line %ld matches '%s'", lineno
+ 1, view
->grep
);
337 report("No match found for '%s'", view
->grep
);
341 search_view(struct view
*view
, enum request request
)
344 int regex_flags
= opt_ignore_case
? REG_ICASE
: 0;
347 regfree(view
->regex
);
350 view
->regex
= calloc(1, sizeof(*view
->regex
));
355 regex_err
= regcomp(view
->regex
, view
->env
->search
, REG_EXTENDED
| regex_flags
);
356 if (regex_err
!= 0) {
357 char buf
[SIZEOF_STR
] = "unknown error";
359 regerror(regex_err
, view
->regex
, buf
, sizeof(buf
));
360 report("Search failed: %s", buf
);
364 string_copy(view
->grep
, view
->env
->search
);
366 find_next(view
, request
);
374 view_history_is_empty(struct view_history
*history
)
376 return !history
->stack
;
380 push_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
382 struct view_state
*state
= history
->stack
;
384 if (state
&& data
&& history
->state_alloc
&&
385 !memcmp(state
->data
, data
, history
->state_alloc
))
388 state
= calloc(1, sizeof(*state
) + history
->state_alloc
);
392 state
->prev
= history
->stack
;
393 history
->stack
= state
;
394 clear_position(&history
->position
);
395 state
->position
= *position
;
396 state
->data
= &state
[1];
397 if (data
&& history
->state_alloc
)
398 memcpy(state
->data
, data
, history
->state_alloc
);
403 pop_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
405 struct view_state
*state
= history
->stack
;
407 if (view_history_is_empty(history
))
410 history
->position
= state
->position
;
411 history
->stack
= state
->prev
;
413 if (data
&& history
->state_alloc
)
414 memcpy(data
, state
->data
, history
->state_alloc
);
416 *position
= state
->position
;
423 reset_view_history(struct view_history
*history
)
425 while (pop_view_history_state(history
, NULL
, NULL
))
430 * Incremental updating
434 reset_view(struct view
*view
)
438 for (i
= 0; i
< view
->lines
; i
++)
439 free(view
->line
[i
].data
);
442 view
->prev_pos
= view
->pos
;
443 clear_position(&view
->pos
);
448 view
->custom_lines
= 0;
449 view
->update_secs
= 0;
453 restore_view_position(struct view
*view
)
455 /* A view without a previous view is the first view */
456 if (!view
->prev
&& view
->env
->lineno
&& view
->env
->lineno
<= view
->lines
) {
457 select_view_line(view
, view
->env
->lineno
);
458 view
->env
->lineno
= 0;
461 /* Ensure that the view position is in a valid state. */
462 if (!check_position(&view
->prev_pos
) ||
463 (view
->pipe
&& view
->lines
<= view
->prev_pos
.lineno
))
464 return goto_view_line(view
, view
->pos
.offset
, view
->pos
.lineno
);
466 /* Changing the view position cancels the restoring. */
467 /* FIXME: Changing back to the first line is not detected. */
468 if (check_position(&view
->pos
)) {
469 clear_position(&view
->prev_pos
);
473 if (goto_view_line(view
, view
->prev_pos
.offset
, view
->prev_pos
.lineno
) &&
474 view_is_displayed(view
))
477 view
->pos
.col
= view
->prev_pos
.col
;
478 clear_position(&view
->prev_pos
);
484 end_update(struct view
*view
, bool force
)
488 while (!view
->ops
->read(view
, NULL
))
498 setup_update(struct view
*view
, const char *vid
)
501 /* XXX: Do not use string_copy_rev(), it copies until first space. */
502 string_ncopy(view
->vid
, vid
, strlen(vid
));
503 view
->pipe
= &view
->io
;
504 view
->start_time
= time(NULL
);
508 view_no_refresh(struct view
*view
, enum open_flags flags
)
510 bool reload
= !!(flags
& OPEN_ALWAYS_LOAD
) || !view
->lines
;
512 return (!reload
&& !strcmp(view
->vid
, view
->ops
->id
)) ||
513 ((flags
& OPEN_REFRESH
) && view
->unrefreshable
);
517 begin_update(struct view
*view
, const char *dir
, const char **argv
, enum open_flags flags
)
519 bool extra
= !!(flags
& (OPEN_EXTRA
));
520 bool refresh
= flags
& (OPEN_REFRESH
| OPEN_PREPARED
| OPEN_STDIN
);
521 bool forward_stdin
= flags
& OPEN_FORWARD_STDIN
;
522 enum io_type io_type
= forward_stdin
? IO_RD_STDIN
: IO_RD
;
524 if (view_no_refresh(view
, flags
))
531 end_update(view
, TRUE
);
534 view
->unrefreshable
= open_in_pager_mode(flags
);
536 if (!refresh
&& argv
) {
537 bool file_filter
= !view_has_flags(view
, VIEW_FILE_FILTER
) || opt_file_filter
;
540 if (!argv_format(view
->env
, &view
->argv
, argv
, !view
->prev
, file_filter
)) {
541 report("Failed to format %s arguments", view
->name
);
545 /* Put the current view ref value to the view title ref
546 * member. This is needed by the blob view. Most other
547 * views sets it automatically after loading because the
548 * first line is a commit line. */
549 string_copy_rev(view
->ref
, view
->ops
->id
);
552 if (view
->argv
&& view
->argv
[0] &&
553 !io_run(&view
->io
, io_type
, view
->dir
, opt_env
, view
->argv
)) {
554 report("Failed to open %s view", view
->name
);
558 if (open_from_stdin(flags
)) {
559 if (!io_open(&view
->io
, "%s", ""))
560 die("Failed to open stdin");
564 setup_update(view
, view
->ops
->id
);
570 update_view(struct view
*view
)
573 /* Clear the view and redraw everything since the tree sorting
574 * might have rearranged things. */
575 bool redraw
= view
->lines
== 0;
576 bool can_read
= TRUE
;
577 struct encoding
*encoding
= view
->encoding
? view
->encoding
: default_encoding
;
582 if (!io_can_read(view
->pipe
, FALSE
)) {
583 if (view
->lines
== 0 && view_is_displayed(view
)) {
584 time_t secs
= time(NULL
) - view
->start_time
;
586 if (secs
> 1 && secs
> view
->update_secs
) {
587 if (view
->update_secs
== 0)
589 update_view_title(view
);
590 view
->update_secs
= secs
;
596 for (; (line
= io_get(view
->pipe
, '\n', can_read
)); can_read
= FALSE
) {
598 line
= encoding_convert(encoding
, line
);
601 if (!view
->ops
->read(view
, line
)) {
602 report("Allocation failure");
603 end_update(view
, TRUE
);
608 if (!view_has_flags(view
, VIEW_CUSTOM_DIGITS
)) {
609 int digits
= count_digits(view
->lines
);
611 /* Keep the displayed view in sync with line number scaling. */
612 if (digits
!= view
->digits
) {
613 view
->digits
= digits
;
614 if (opt_show_line_numbers
|| view_has_flags(view
, VIEW_ALWAYS_LINENO
))
619 if (io_error(view
->pipe
)) {
620 report("Failed to read: %s", io_strerror(view
->pipe
));
621 end_update(view
, TRUE
);
623 } else if (io_eof(view
->pipe
)) {
624 end_update(view
, FALSE
);
627 if (restore_view_position(view
))
630 if (!view_is_displayed(view
))
633 if (redraw
|| view
->force_redraw
)
634 redraw_view_from(view
, 0);
636 redraw_view_dirty(view
);
637 view
->force_redraw
= FALSE
;
639 /* Update the title _after_ the redraw so that if the redraw picks up a
640 * commit reference in view->ref it'll be available here. */
641 update_view_title(view
);
646 update_view_title(struct view
*view
)
648 WINDOW
*window
= view
->title
;
649 struct line
*line
= &view
->line
[view
->pos
.lineno
];
650 unsigned int view_lines
, lines
;
652 assert(view_is_displayed(view
));
654 if (view
== display
[current_view
])
655 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_FOCUS
));
657 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_BLUR
));
660 mvwprintw(window
, 0, 0, "[%s]", view
->name
);
663 wprintw(window
, " %s", view
->ref
);
666 if (!view_has_flags(view
, VIEW_CUSTOM_STATUS
) && view_has_line(view
, line
) &&
668 wprintw(window
, " - %s %d of %zd",
671 view
->lines
- view
->custom_lines
);
675 time_t secs
= time(NULL
) - view
->start_time
;
677 /* Three git seconds are a long time ... */
679 wprintw(window
, " loading %lds", secs
);
682 view_lines
= view
->pos
.offset
+ view
->height
;
683 lines
= view
->lines
? MIN(view_lines
, view
->lines
) * 100 / view
->lines
: 0;
684 mvwprintw(window
, 0, view
->width
- count_digits(lines
) - 1, "%d%%", lines
);
686 wnoutrefresh(window
);
694 split_view(struct view
*prev
, struct view
*view
)
697 current_view
= opt_focus_child
? 1 : 0;
701 if (prev
->pos
.lineno
- prev
->pos
.offset
>= prev
->height
) {
702 /* Take the title line into account. */
703 int lines
= prev
->pos
.lineno
- prev
->pos
.offset
- prev
->height
+ 1;
705 /* Scroll the view that was split if the current line is
706 * outside the new limited view. */
707 do_scroll_view(prev
, lines
);
710 if (view
!= prev
&& view_is_displayed(prev
)) {
711 /* "Blur" the previous view. */
712 update_view_title(prev
);
717 maximize_view(struct view
*view
, bool redraw
)
719 memset(display
, 0, sizeof(display
));
721 display
[current_view
] = view
;
724 redraw_display(FALSE
);
730 load_view(struct view
*view
, struct view
*prev
, enum open_flags flags
)
732 bool refresh
= !view_no_refresh(view
, flags
);
734 /* When prev == view it means this is the first loaded view. */
735 if (prev
&& view
!= prev
) {
741 end_update(view
, TRUE
);
742 if (view
->ops
->private_size
) {
743 if (!view
->private) {
744 view
->private = calloc(1, view
->ops
->private_size
);
747 view
->ops
->done(view
);
748 memset(view
->private, 0, view
->ops
->private_size
);
752 if (view
->ops
->columns_size
) {
753 if (!view
->columns_info
)
754 view
->columns_info
= calloc(1, sizeof(*view
->columns_info
)
755 * view
->ops
->columns_size
);
757 memset(view
->columns_info
, 0, sizeof(*view
->columns_info
)
758 * view
->ops
->columns_size
);
759 view_columns_info_init(view
);
762 if (!view
->ops
->open(view
, flags
))
767 bool split
= !!(flags
& OPEN_SPLIT
);
770 split_view(prev
, view
);
772 maximize_view(view
, FALSE
);
776 restore_view_position(view
);
778 if (view
->pipe
&& view
->lines
== 0) {
779 /* Clear the old view and let the incremental updating refill
782 if (!(flags
& (OPEN_RELOAD
| OPEN_REFRESH
)))
783 clear_position(&view
->prev_pos
);
785 } else if (view_is_displayed(view
)) {
791 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
792 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
795 open_view(struct view
*prev
, struct view
*view
, enum open_flags flags
)
797 bool reload
= !!(flags
& (OPEN_RELOAD
| OPEN_PREPARED
));
798 int nviews
= displayed_views();
800 assert(flags
^ OPEN_REFRESH
);
802 if (view
== prev
&& nviews
== 1 && !reload
) {
803 report("Already in %s view", view
->name
);
807 if (!view_has_flags(view
, VIEW_NO_GIT_DIR
) && !repo
.git_dir
[0]) {
808 report("The %s view is disabled in pager mode", view
->name
);
813 view
->keymap
= get_keymap(view
->name
, strlen(view
->name
));
814 load_view(view
, prev
? prev
: view
, flags
);
818 open_argv(struct view
*prev
, struct view
*view
, const char *argv
[], const char *dir
, enum open_flags flags
)
821 end_update(view
, TRUE
);
824 if (!argv_copy(&view
->argv
, argv
)) {
825 report("Failed to open %s view: %s", view
->name
, io_strerror(&view
->io
));
827 open_view(prev
, view
, flags
| OPEN_PREPARED
);
835 static struct view
*sorting_view
;
837 #define sort_order_reverse(state, result) \
838 ((state)->reverse ? -(result) : (result))
840 #define sort_order(state, cmp, o1, o2) \
841 sort_order_reverse(state, (!(o1) || !(o2)) ? !!(o2) - !!(o1) : cmp(o1, o2))
843 #define number_compare(size1, size2) (*(size2) - *(size1))
844 #define ref_compare(ref1, ref2) strcmp((ref1)->name, (ref2)->name)
847 sort_view_compare(const void *l1
, const void *l2
)
849 const struct line
*line1
= l1
;
850 const struct line
*line2
= l2
;
851 struct view_columns columns1
= {};
852 struct view_columns columns2
= {};
853 struct sort_state
*sort
= &sorting_view
->sort
;
855 if (!sorting_view
->ops
->get_columns(sorting_view
, line1
, &columns1
))
857 else if (!sorting_view
->ops
->get_columns(sorting_view
, line2
, &columns2
))
860 switch (get_sort_field(sorting_view
)) {
861 case VIEW_COLUMN_AUTHOR
:
862 return sort_order(sort
, ident_compare
, columns1
.author
, columns2
.author
);
864 case VIEW_COLUMN_DATE
:
865 return sort_order(sort
, timecmp
, columns1
.date
, columns2
.date
);
868 return sort_order(sort
, strcmp
, columns1
.id
, columns2
.id
);
870 case VIEW_COLUMN_FILE_NAME
:
871 if (columns1
.mode
!= columns2
.mode
)
872 return sort_order_reverse(sort
, S_ISDIR(*columns1
.mode
) ? -1 : 1);
873 return sort_order(sort
, strcmp
, columns1
.file_name
, columns2
.file_name
);
875 case VIEW_COLUMN_FILE_SIZE
:
876 return sort_order(sort
, number_compare
, columns1
.file_size
, columns2
.file_size
);
878 case VIEW_COLUMN_LINE_NUMBER
:
879 return sort_order_reverse(sort
, line2
->lineno
- line1
->lineno
);
881 case VIEW_COLUMN_MODE
:
882 return sort_order(sort
, number_compare
, columns1
.mode
, columns2
.mode
);
884 case VIEW_COLUMN_REF
:
885 return sort_order(sort
, ref_compare
, columns1
.ref
, columns2
.ref
);
887 case VIEW_COLUMN_COMMIT_TITLE
:
888 return sort_order(sort
, strcmp
, columns1
.commit_title
, columns2
.commit_title
);
890 case VIEW_COLUMN_TEXT
:
891 return sort_order(sort
, strcmp
, columns1
.text
, columns2
.text
);
893 case VIEW_COLUMN_REFS
:
894 case VIEW_COLUMN_GRAPH
:
895 die("Unsupported search: %d", get_sort_field(sorting_view
));
902 sort_view(struct view
*view
, bool change_field
)
904 struct sort_state
*state
= &view
->sort
;
908 state
->current
= (state
->current
+ 1) % view
->ops
->columns_size
;
909 if (get_sort_field(view
) == VIEW_COLUMN_ID
&& !opt_show_id
)
911 if (get_sort_field(view
) == VIEW_COLUMN_REFS
)
913 if (get_sort_field(view
) == VIEW_COLUMN_GRAPH
)
918 state
->reverse
= !state
->reverse
;
922 qsort(view
->line
, view
->lines
, sizeof(*view
->line
), sort_view_compare
);
926 grep_refs(struct view
*view
, const struct ref_list
*list
)
931 if (!opt_show_refs
|| !list
)
934 for (i
= 0; i
< list
->size
; i
++) {
935 if (!regexec(view
->regex
, list
->refs
[i
]->name
, 1, &pmatch
, 0))
943 view_columns_grep(struct view
*view
, struct line
*line
)
945 struct view_columns columns
= {};
946 bool has_columns
= view
->ops
->get_columns(view
, line
, &columns
);
947 const char *text
[] = {
948 has_columns
&& columns
.author
? mkauthor(columns
.author
, opt_author_width
, opt_show_author
) : "",
949 has_columns
&& columns
.date
? mkdate(columns
.date
, opt_show_date
) : "",
950 has_columns
&& columns
.file_name
? columns
.file_name
: "",
951 has_columns
&& columns
.file_size
? mkfilesize(*columns
.file_size
, opt_show_file_size
) : "",
952 has_columns
&& columns
.id
&& opt_show_id
? columns
.id
: "",
953 has_columns
&& columns
.mode
? mkmode(*columns
.mode
) : "",
954 has_columns
&& columns
.commit_title
? columns
.commit_title
: "",
955 has_columns
&& columns
.ref
? columns
.ref
->name
: "",
956 has_columns
&& columns
.text
? columns
.text
: "",
960 if (has_columns
&& grep_refs(view
, columns
.refs
))
963 return grep_text(view
, text
);
967 view_columns_info_changed(struct view
*view
, bool update
)
969 bool changed
= FALSE
;
972 for (i
= 0; i
< view
->ops
->columns_size
; i
++) {
973 enum view_column column
= view
->ops
->columns
[i
];
974 struct column_info
*info
= &view
->columns_info
[i
];
975 unsigned long option
= 0;
978 case VIEW_COLUMN_AUTHOR
:
979 option
= opt_show_author
;
982 case VIEW_COLUMN_DATE
:
983 option
= opt_show_date
;
986 case VIEW_COLUMN_FILE_SIZE
:
987 option
= opt_show_file_size
;
990 case VIEW_COLUMN_GRAPH
:
991 option
= opt_show_rev_graph
;
994 case VIEW_COLUMN_LINE_NUMBER
:
995 option
= opt_show_line_numbers
;
998 case VIEW_COLUMN_REFS
:
999 option
= opt_show_refs
;
1002 case VIEW_COLUMN_ID
:
1003 option
= opt_show_id
;
1006 case VIEW_COLUMN_COMMIT_TITLE
:
1007 case VIEW_COLUMN_FILE_NAME
:
1008 case VIEW_COLUMN_MODE
:
1009 case VIEW_COLUMN_REF
:
1010 case VIEW_COLUMN_TEXT
:
1014 if (option
!= info
->option
) {
1017 info
->option
= option
;
1026 view_columns_info_init(struct view
*view
)
1030 view_columns_info_changed(view
, TRUE
);
1031 for (i
= 0; i
< view
->ops
->columns_size
; i
++)
1032 view
->columns_info
[i
].width
= 0;
1036 view_columns_info_update(struct view
*view
, struct line
*line
)
1038 struct view_columns columns
= {};
1039 bool changed
= FALSE
;
1042 if (!view
->ops
->get_columns(view
, line
, &columns
))
1045 for (i
= 0; i
< view
->ops
->columns_size
; i
++) {
1046 enum view_column column
= view
->ops
->columns
[i
];
1047 const char *text
= NULL
;
1050 case VIEW_COLUMN_AUTHOR
:
1052 text
= mkauthor(columns
.author
, opt_author_width
, opt_show_author
);
1055 case VIEW_COLUMN_DATE
:
1057 text
= mkdate(columns
.date
, opt_show_date
);
1060 case VIEW_COLUMN_REF
:
1062 text
= columns
.ref
->name
;
1065 case VIEW_COLUMN_FILE_NAME
:
1066 if (columns
.file_name
)
1067 text
= columns
.file_name
;
1070 case VIEW_COLUMN_FILE_SIZE
:
1071 if (columns
.file_size
)
1072 text
= mkfilesize(*columns
.file_size
, opt_show_file_size
);
1075 case VIEW_COLUMN_ID
:
1076 if (columns
.id
&& !iscommit(columns
.id
))
1080 case VIEW_COLUMN_COMMIT_TITLE
:
1081 case VIEW_COLUMN_GRAPH
:
1082 case VIEW_COLUMN_LINE_NUMBER
:
1083 case VIEW_COLUMN_MODE
:
1084 case VIEW_COLUMN_REFS
:
1085 case VIEW_COLUMN_TEXT
:
1090 int width
= utf8_width(text
);
1092 if (width
> view
->columns_info
[i
].width
) {
1093 view
->columns_info
[i
].width
= width
;
1100 view
->force_redraw
= TRUE
;
1105 view_columns_draw(struct view
*view
, struct line
*line
, unsigned int lineno
)
1107 struct view_columns columns
= {};
1110 if (!view
->ops
->get_columns(view
, line
, &columns
))
1113 for (i
= 0; i
< view
->ops
->columns_size
; i
++) {
1114 enum view_column column
= view
->ops
->columns
[i
];
1115 int width
= view
->columns_info
[i
].width
;
1118 case VIEW_COLUMN_DATE
:
1119 if (draw_date(view
, columns
.date
))
1123 case VIEW_COLUMN_AUTHOR
:
1124 if (draw_author(view
, columns
.author
, opt_author_width
? opt_author_width
: width
))
1128 case VIEW_COLUMN_REF
:
1130 const struct ref
*ref
= columns
.ref
;
1131 enum line_type type
= !ref
|| !ref
->valid
? LINE_DEFAULT
: get_line_type_from_ref(ref
);
1132 const char *name
= ref
? ref
->name
: NULL
;
1134 if (draw_field(view
, type
, name
, width
, ALIGN_LEFT
, FALSE
))
1139 case VIEW_COLUMN_REFS
:
1140 if (draw_refs(view
, columns
.refs
))
1144 case VIEW_COLUMN_GRAPH
:
1145 if (columns
.graph
&& draw_graph(view
, columns
.graph
))
1149 case VIEW_COLUMN_ID
:
1150 if (!width
&& draw_id(view
, columns
.id
))
1152 else if (opt_show_id
&& draw_id_custom(view
, LINE_ID
, columns
.id
, width
))
1156 case VIEW_COLUMN_LINE_NUMBER
:
1157 if (draw_lineno(view
, lineno
))
1161 case VIEW_COLUMN_MODE
:
1162 if (draw_mode(view
, columns
.mode
? *columns
.mode
: 0))
1166 case VIEW_COLUMN_FILE_SIZE
:
1167 if (draw_file_size(view
, columns
.file_size
? *columns
.file_size
: 0, width
, !columns
.mode
|| S_ISDIR(*columns
.mode
)))
1171 case VIEW_COLUMN_COMMIT_TITLE
:
1172 if (draw_commit_title(view
, columns
.commit_title
, 0))
1176 case VIEW_COLUMN_FILE_NAME
:
1177 if (draw_filename(view
, columns
.file_name
, TRUE
,
1178 opt_show_filename_width
? opt_show_filename_width
: width
))
1183 case VIEW_COLUMN_TEXT
:
1184 if (draw_text(view
, line
->type
, columns
.text
))
1194 find_line_by_type(struct view
*view
, struct line
*line
, enum line_type type
, int direction
)
1196 for (; view_has_line(view
, line
); line
+= direction
)
1197 if (line
->type
== type
)
1207 DEFINE_ALLOCATOR(realloc_lines
, struct line
, 256)
1210 add_line_at(struct view
*view
, unsigned long pos
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1213 unsigned long lineno
;
1215 if (!realloc_lines(&view
->line
, view
->lines
, 1))
1219 void *alloc_data
= calloc(1, data_size
);
1225 memcpy(alloc_data
, data
, data_size
);
1229 if (pos
< view
->lines
) {
1231 line
= view
->line
+ pos
;
1232 lineno
= line
->lineno
;
1234 memmove(line
+ 1, line
, (view
->lines
- pos
) * sizeof(*view
->line
));
1235 while (pos
< view
->lines
) {
1236 view
->line
[pos
].lineno
++;
1237 view
->line
[pos
++].dirty
= 1;
1240 line
= &view
->line
[view
->lines
++];
1241 lineno
= view
->lines
- view
->custom_lines
;
1244 memset(line
, 0, sizeof(*line
));
1246 line
->data
= (void *) data
;
1250 view
->custom_lines
++;
1252 line
->lineno
= lineno
;
1258 add_line(struct view
*view
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1260 return add_line_at(view
, view
->lines
, data
, type
, data_size
, custom
);
1264 add_line_alloc_(struct view
*view
, void **ptr
, enum line_type type
, size_t data_size
, bool custom
)
1266 struct line
*line
= add_line(view
, NULL
, type
, data_size
, custom
);
1274 add_line_nodata(struct view
*view
, enum line_type type
)
1276 return add_line(view
, NULL
, type
, 0, FALSE
);
1280 add_line_text(struct view
*view
, const char *text
, enum line_type type
)
1282 return add_line(view
, text
, type
, strlen(text
) + 1, FALSE
);
1285 struct line
* PRINTF_LIKE(3, 4)
1286 add_line_format(struct view
*view
, enum line_type type
, const char *fmt
, ...)
1288 char buf
[SIZEOF_STR
];
1291 FORMAT_BUFFER(buf
, sizeof(buf
), fmt
, retval
, FALSE
);
1292 return retval
>= 0 ? add_line_text(view
, buf
, type
) : NULL
;
1296 * Global view state.
1299 /* Included last to not pollute the rest of the file. */
1300 #include "tig/main.h"
1301 #include "tig/diff.h"
1302 #include "tig/log.h"
1303 #include "tig/tree.h"
1304 #include "tig/blob.h"
1305 #include "tig/blame.h"
1306 #include "tig/branch.h"
1307 #include "tig/status.h"
1308 #include "tig/stage.h"
1309 #include "tig/stash.h"
1310 #include "tig/grep.h"
1311 #include "tig/pager.h"
1312 #include "tig/help.h"
1314 static struct view
*views
[] = {
1315 #define VIEW_DATA(id, name) &name##_view
1316 VIEW_INFO(VIEW_DATA
)
1322 return 0 <= i
&& i
< ARRAY_SIZE(views
) ? views
[i
] : NULL
;
1325 /* vim: set ts=8 sw=8 noexpandtab: */