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/watch.h"
18 #include "tig/options.h"
21 #include "tig/display.h"
28 goto_view_line(struct view
*view
, unsigned long offset
, unsigned long lineno
)
30 if (lineno
>= view
->lines
)
31 lineno
= view
->lines
> 0 ? view
->lines
- 1 : 0;
33 if (offset
> lineno
|| offset
+ view
->height
<= lineno
) {
34 unsigned long half
= view
->height
/ 2;
37 offset
= lineno
- half
;
42 if (offset
!= view
->pos
.offset
|| lineno
!= view
->pos
.lineno
) {
43 view
->pos
.offset
= offset
;
44 view
->pos
.lineno
= lineno
;
51 /* Scrolling backend */
53 do_scroll_view(struct view
*view
, int lines
)
55 bool redraw_current_line
= FALSE
;
57 /* The rendering expects the new offset. */
58 view
->pos
.offset
+= lines
;
60 assert(0 <= view
->pos
.offset
&& view
->pos
.offset
< view
->lines
);
63 /* Move current line into the view. */
64 if (view
->pos
.lineno
< view
->pos
.offset
) {
65 view
->pos
.lineno
= view
->pos
.offset
;
66 redraw_current_line
= TRUE
;
67 } else if (view
->pos
.lineno
>= view
->pos
.offset
+ view
->height
) {
68 view
->pos
.lineno
= view
->pos
.offset
+ view
->height
- 1;
69 redraw_current_line
= TRUE
;
72 assert(view
->pos
.offset
<= view
->pos
.lineno
&& view
->pos
.lineno
< view
->lines
);
74 /* Redraw the whole screen if scrolling is pointless. */
75 if (view
->height
< ABS(lines
)) {
79 int line
= lines
> 0 ? view
->height
- lines
: 0;
80 int end
= line
+ ABS(lines
);
82 scrollok(view
->win
, TRUE
);
83 wscrl(view
->win
, lines
);
84 scrollok(view
->win
, FALSE
);
86 while (line
< end
&& draw_view_line(view
, line
))
89 if (redraw_current_line
)
90 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
91 wnoutrefresh(view
->win
);
94 view
->has_scrolled
= TRUE
;
100 scroll_view(struct view
*view
, enum request request
)
104 assert(view_is_displayed(view
));
106 if (request
== REQ_SCROLL_WHEEL_DOWN
|| request
== REQ_SCROLL_WHEEL_UP
)
107 lines
= opt_mouse_scroll
;
110 case REQ_SCROLL_FIRST_COL
:
112 redraw_view_from(view
, 0);
115 case REQ_SCROLL_LEFT
:
116 if (view
->pos
.col
== 0) {
117 report("Cannot scroll beyond the first column");
120 if (view
->pos
.col
<= apply_step(opt_horizontal_scroll
, view
->width
))
123 view
->pos
.col
-= apply_step(opt_horizontal_scroll
, view
->width
);
124 redraw_view_from(view
, 0);
127 case REQ_SCROLL_RIGHT
:
128 view
->pos
.col
+= apply_step(opt_horizontal_scroll
, view
->width
);
132 case REQ_SCROLL_PAGE_DOWN
:
133 lines
= view
->height
;
134 case REQ_SCROLL_WHEEL_DOWN
:
135 case REQ_SCROLL_LINE_DOWN
:
136 if (view
->pos
.offset
+ lines
> view
->lines
)
137 lines
= view
->lines
- view
->pos
.offset
;
139 if (lines
== 0 || view
->pos
.offset
+ view
->height
>= view
->lines
) {
140 report("Cannot scroll beyond the last line");
145 case REQ_SCROLL_PAGE_UP
:
146 lines
= view
->height
;
147 case REQ_SCROLL_LINE_UP
:
148 case REQ_SCROLL_WHEEL_UP
:
149 if (lines
> view
->pos
.offset
)
150 lines
= view
->pos
.offset
;
153 report("Cannot scroll beyond the first line");
161 die("request %d not handled in switch", request
);
164 do_scroll_view(view
, lines
);
169 move_view(struct view
*view
, enum request request
)
171 int scroll_steps
= 0;
175 case REQ_MOVE_FIRST_LINE
:
176 steps
= -view
->pos
.lineno
;
179 case REQ_MOVE_LAST_LINE
:
180 steps
= view
->lines
- view
->pos
.lineno
- 1;
183 case REQ_MOVE_PAGE_UP
:
184 steps
= view
->height
> view
->pos
.lineno
185 ? -view
->pos
.lineno
: -view
->height
;
188 case REQ_MOVE_PAGE_DOWN
:
189 steps
= view
->pos
.lineno
+ view
->height
>= view
->lines
190 ? view
->lines
- view
->pos
.lineno
- 1 : view
->height
;
193 case REQ_MOVE_HALF_PAGE_UP
:
194 steps
= view
->height
/ 2 > view
->pos
.lineno
195 ? -view
->pos
.lineno
: -(view
->height
/ 2);
198 case REQ_MOVE_HALF_PAGE_DOWN
:
199 steps
= view
->pos
.lineno
+ view
->height
/ 2 >= view
->lines
200 ? view
->lines
- view
->pos
.lineno
- 1 : view
->height
/ 2;
214 die("request %d not handled in switch", request
);
217 if (steps
<= 0 && view
->pos
.lineno
== 0) {
218 report("Cannot move beyond the first line");
221 } else if (steps
>= 0 && view
->pos
.lineno
+ 1 >= view
->lines
) {
222 report("Cannot move beyond the last line");
226 /* Move the current line */
227 view
->pos
.lineno
+= steps
;
228 assert(0 <= view
->pos
.lineno
&& view
->pos
.lineno
< view
->lines
);
230 /* Check whether the view needs to be scrolled */
231 if (view
->pos
.lineno
< view
->pos
.offset
||
232 view
->pos
.lineno
>= view
->pos
.offset
+ view
->height
) {
233 scroll_steps
= steps
;
234 if (steps
< 0 && -steps
> view
->pos
.offset
) {
235 scroll_steps
= -view
->pos
.offset
;
237 } else if (steps
> 0) {
238 if (view
->pos
.lineno
== view
->lines
- 1 &&
239 view
->lines
> view
->height
) {
240 scroll_steps
= view
->lines
- view
->pos
.offset
- 1;
241 if (scroll_steps
>= view
->height
)
242 scroll_steps
-= view
->height
- 1;
247 if (!view_is_displayed(view
)) {
248 view
->pos
.offset
+= scroll_steps
;
249 assert(0 <= view
->pos
.offset
&& view
->pos
.offset
< view
->lines
);
250 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
254 /* Repaint the old "current" line if we be scrolling */
255 if (ABS(steps
) < view
->height
)
256 draw_view_line(view
, view
->pos
.lineno
- steps
- view
->pos
.offset
);
259 do_scroll_view(view
, scroll_steps
);
263 /* Draw the current line */
264 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
266 wnoutrefresh(view
->win
);
274 DEFINE_ALLOCATOR(realloc_unsigned_ints
, unsigned int, 32)
277 grep_text(struct view
*view
, const char *text
[])
282 for (i
= 0; text
[i
]; i
++)
283 if (*text
[i
] && !regexec(view
->regex
, text
[i
], 1, &pmatch
, 0))
289 select_view_line(struct view
*view
, unsigned long lineno
)
291 struct position old
= view
->pos
;
293 if (goto_view_line(view
, view
->pos
.offset
, lineno
)) {
294 if (view_is_displayed(view
)) {
295 if (old
.offset
!= view
->pos
.offset
) {
298 draw_view_line(view
, old
.lineno
- view
->pos
.offset
);
299 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
300 wnoutrefresh(view
->win
);
303 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
309 find_matches(struct view
*view
)
313 /* Note, lineno is unsigned long so will wrap around in which case it
314 * will become bigger than view->lines. */
315 for (lineno
= 0; lineno
< view
->lines
; lineno
++) {
316 if (!view
->ops
->grep(view
, &view
->line
[lineno
]))
319 if (!realloc_unsigned_ints(&view
->matched_line
, view
->matched_lines
, 1))
322 view
->matched_line
[view
->matched_lines
++] = lineno
;
329 find_next(struct view
*view
, enum request request
)
335 if (!*view
->env
->search
)
336 report("No previous search");
338 search_view(view
, request
);
348 case REQ_SEARCH_BACK
:
357 if (!view
->matched_lines
&& !find_matches(view
)) {
358 report("Allocation failure");
362 /* Note, `i` is unsigned and will wrap around in which case it
363 * will become bigger than view->matched_lines. */
364 i
= direction
> 0 ? 0 : view
->matched_lines
- 1;
365 for (; i
< view
->matched_lines
; i
+= direction
) {
366 size_t lineno
= view
->matched_line
[i
];
368 if (direction
> 0 && lineno
<= view
->pos
.lineno
)
371 if (direction
< 0 && lineno
>= view
->pos
.lineno
)
374 select_view_line(view
, lineno
);
375 report("Line %zu matches '%s' (%zu of %zu)", lineno
+ 1, view
->grep
, i
+ 1, view
->matched_lines
);
379 report("No match found for '%s'", view
->grep
);
383 reset_matches(struct view
*view
)
385 free(view
->matched_line
);
386 view
->matched_line
= NULL
;
387 view
->matched_lines
= 0;
391 search_view(struct view
*view
, enum request request
)
394 int regex_flags
= opt_ignore_case
? REG_ICASE
: 0;
397 regfree(view
->regex
);
400 view
->regex
= calloc(1, sizeof(*view
->regex
));
405 regex_err
= regcomp(view
->regex
, view
->env
->search
, REG_EXTENDED
| regex_flags
);
406 if (regex_err
!= 0) {
407 char buf
[SIZEOF_STR
] = "unknown error";
409 regerror(regex_err
, view
->regex
, buf
, sizeof(buf
));
410 report("Search failed: %s", buf
);
414 string_copy(view
->grep
, view
->env
->search
);
418 find_next(view
, request
);
426 view_history_is_empty(struct view_history
*history
)
428 return !history
->stack
;
432 push_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
434 struct view_state
*state
= history
->stack
;
436 if (state
&& data
&& history
->state_alloc
&&
437 !memcmp(state
->data
, data
, history
->state_alloc
))
440 state
= calloc(1, sizeof(*state
) + history
->state_alloc
);
444 state
->prev
= history
->stack
;
445 history
->stack
= state
;
446 clear_position(&history
->position
);
447 state
->position
= *position
;
448 state
->data
= &state
[1];
449 if (data
&& history
->state_alloc
)
450 memcpy(state
->data
, data
, history
->state_alloc
);
455 pop_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
457 struct view_state
*state
= history
->stack
;
459 if (view_history_is_empty(history
))
462 history
->position
= state
->position
;
463 history
->stack
= state
->prev
;
465 if (data
&& history
->state_alloc
)
466 memcpy(data
, state
->data
, history
->state_alloc
);
468 *position
= state
->position
;
475 reset_view_history(struct view_history
*history
)
477 while (pop_view_history_state(history
, NULL
, NULL
))
482 * Incremental updating
486 reset_view(struct view
*view
)
490 for (i
= 0; i
< view
->lines
; i
++)
491 free(view
->line
[i
].data
);
495 view
->prev_pos
= view
->pos
;
496 /* A view without a previous view is the first view */
497 if (!view
->prev
&& !view
->lines
&& view
->prev_pos
.lineno
== 0)
498 view
->prev_pos
.lineno
= view
->env
->lineno
;
499 clear_position(&view
->pos
);
502 view_column_reset(view
);
507 view
->custom_lines
= 0;
508 view
->update_secs
= 0;
512 restore_view_position(struct view
*view
)
514 /* Ensure that the view position is in a valid state. */
515 if (!check_position(&view
->prev_pos
) ||
516 (view
->pipe
&& view
->lines
<= view
->prev_pos
.lineno
))
517 return goto_view_line(view
, view
->pos
.offset
, view
->pos
.lineno
);
519 /* Changing the view position cancels the restoring. */
520 /* FIXME: Changing back to the first line is not detected. */
521 if (check_position(&view
->pos
)) {
522 clear_position(&view
->prev_pos
);
526 if (goto_view_line(view
, view
->prev_pos
.offset
, view
->prev_pos
.lineno
) &&
527 view_is_displayed(view
))
530 view
->pos
.col
= view
->prev_pos
.col
;
531 clear_position(&view
->prev_pos
);
537 end_update(struct view
*view
, bool force
)
541 while (!view
->ops
->read(view
, NULL
))
551 setup_update(struct view
*view
, const char *vid
)
554 /* XXX: Do not use string_copy_rev(), it copies until first space. */
555 string_ncopy(view
->vid
, vid
, strlen(vid
));
556 view
->pipe
= &view
->io
;
557 view
->start_time
= time(NULL
);
561 view_no_refresh(struct view
*view
, enum open_flags flags
)
563 bool reload
= !!(flags
& OPEN_ALWAYS_LOAD
) || !view
->lines
;
565 return (!reload
&& !strcmp(view
->vid
, view
->ops
->id
)) ||
566 ((flags
& OPEN_REFRESH
) && !view_can_refresh(view
));
570 begin_update(struct view
*view
, const char *dir
, const char **argv
, enum open_flags flags
)
572 bool extra
= !!(flags
& (OPEN_EXTRA
));
573 bool refresh
= flags
& (OPEN_REFRESH
| OPEN_PREPARED
| OPEN_STDIN
);
574 enum io_flags forward_stdin
= (flags
& OPEN_FORWARD_STDIN
) ? IO_RD_FORWARD_STDIN
: 0;
575 enum io_flags with_stderr
= (flags
& OPEN_WITH_STDERR
) ? IO_RD_WITH_STDERR
: 0;
576 enum io_flags io_flags
= forward_stdin
| with_stderr
;
578 if (view_no_refresh(view
, flags
))
585 end_update(view
, TRUE
);
588 view
->unrefreshable
= open_in_pager_mode(flags
);
590 if (!refresh
&& argv
) {
591 bool file_filter
= !view_has_flags(view
, VIEW_FILE_FILTER
) || opt_file_filter
;
594 if (!argv_format(view
->env
, &view
->argv
, argv
, !view
->prev
, file_filter
)) {
595 report("Failed to format %s arguments", view
->name
);
599 /* Put the current view ref value to the view title ref
600 * member. This is needed by the blob view. Most other
601 * views sets it automatically after loading because the
602 * first line is a commit line. */
603 string_copy_rev(view
->ref
, view
->ops
->id
);
606 if (view
->argv
&& view
->argv
[0] &&
607 !io_exec(&view
->io
, IO_RD
, view
->dir
, opt_env
, view
->argv
, io_flags
)) {
608 report("Failed to open %s view", view
->name
);
612 if (open_from_stdin(flags
)) {
613 if (!io_open(&view
->io
, "%s", ""))
614 die("Failed to open stdin");
618 setup_update(view
, view
->ops
->id
);
624 update_view(struct view
*view
)
626 /* Clear the view and redraw everything since the tree sorting
627 * might have rearranged things. */
628 bool redraw
= view
->lines
== 0;
629 bool can_read
= TRUE
;
630 struct encoding
*encoding
= view
->encoding
? view
->encoding
: default_encoding
;
636 if (!io_can_read(view
->pipe
, FALSE
)) {
637 if (view
->lines
== 0 && view_is_displayed(view
)) {
638 time_t secs
= time(NULL
) - view
->start_time
;
640 if (secs
> 1 && secs
> view
->update_secs
) {
641 if (view
->update_secs
== 0)
643 update_view_title(view
);
644 view
->update_secs
= secs
;
650 for (; io_get(view
->pipe
, &line
, '\n', can_read
); can_read
= FALSE
) {
651 if (encoding
&& !encoding_convert(encoding
, &line
)) {
652 report("Encoding failure");
653 end_update(view
, TRUE
);
657 if (!view
->ops
->read(view
, &line
)) {
658 report("Allocation failure");
659 end_update(view
, TRUE
);
664 if (io_error(view
->pipe
)) {
665 report("Failed to read: %s", io_strerror(view
->pipe
));
666 end_update(view
, TRUE
);
668 } else if (io_eof(view
->pipe
)) {
669 end_update(view
, FALSE
);
672 if (restore_view_position(view
))
675 if (!view_is_displayed(view
))
678 if (redraw
|| view
->force_redraw
)
679 redraw_view_from(view
, 0);
681 redraw_view_dirty(view
);
682 view
->force_redraw
= FALSE
;
684 /* Update the title _after_ the redraw so that if the redraw picks up a
685 * commit reference in view->ref it'll be available here. */
686 update_view_title(view
);
691 update_view_title(struct view
*view
)
693 WINDOW
*window
= view
->title
;
694 struct line
*line
= &view
->line
[view
->pos
.lineno
];
695 unsigned int view_lines
, lines
;
697 assert(view_is_displayed(view
));
699 if (view
== display
[current_view
])
700 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_FOCUS
));
702 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_BLUR
));
705 mvwprintw(window
, 0, 0, "[%s]", view
->name
);
708 wprintw(window
, " %s", view
->ref
);
711 if (!view_has_flags(view
, VIEW_CUSTOM_STATUS
) && view_has_line(view
, line
) &&
713 wprintw(window
, " - %s %d of %zd",
716 view
->lines
- view
->custom_lines
);
720 time_t secs
= time(NULL
) - view
->start_time
;
722 /* Three git seconds are a long time ... */
724 wprintw(window
, " loading %lds", secs
);
727 view_lines
= view
->pos
.offset
+ view
->height
;
728 lines
= view
->lines
? MIN(view_lines
, view
->lines
) * 100 / view
->lines
: 0;
729 mvwprintw(window
, 0, view
->width
- count_digits(lines
) - 1, "%d%%", lines
);
731 wnoutrefresh(window
);
739 split_view(struct view
*prev
, struct view
*view
)
742 current_view
= opt_focus_child
? 1 : 0;
746 if (prev
->pos
.lineno
- prev
->pos
.offset
>= prev
->height
) {
747 /* Take the title line into account. */
748 int lines
= prev
->pos
.lineno
- prev
->pos
.offset
- prev
->height
+ 1;
750 /* Scroll the view that was split if the current line is
751 * outside the new limited view. */
752 do_scroll_view(prev
, lines
);
755 if (view
!= prev
&& view_is_displayed(prev
)) {
756 /* "Blur" the previous view. */
757 update_view_title(prev
);
762 maximize_view(struct view
*view
, bool redraw
)
764 memset(display
, 0, sizeof(display
));
766 display
[current_view
] = view
;
769 redraw_display(FALSE
);
775 load_view(struct view
*view
, struct view
*prev
, enum open_flags flags
)
777 bool refresh
= !view_no_refresh(view
, flags
);
779 /* When prev == view it means this is the first loaded view. */
780 if (prev
&& view
!= prev
) {
784 if (!refresh
&& view_can_refresh(view
) &&
785 watch_update_single(&view
->watch
, WATCH_EVENT_SWITCH_VIEW
)) {
786 refresh
= watch_dirty(&view
->watch
);
788 flags
|= OPEN_REFRESH
;
793 end_update(view
, TRUE
);
794 if (view
->ops
->private_size
) {
795 if (!view
->private) {
796 view
->private = calloc(1, view
->ops
->private_size
);
799 view
->ops
->done(view
);
800 memset(view
->private, 0, view
->ops
->private_size
);
804 if (!view
->ops
->open(view
, flags
))
809 bool split
= !!(flags
& OPEN_SPLIT
);
812 split_view(prev
, view
);
814 maximize_view(view
, FALSE
);
818 restore_view_position(view
);
820 if (view
->pipe
&& view
->lines
== 0) {
821 /* Clear the old view and let the incremental updating refill
824 /* Do not clear the position if it is the first view. */
825 if (view
->prev
&& !(flags
& (OPEN_RELOAD
| OPEN_REFRESH
)))
826 clear_position(&view
->prev_pos
);
828 } else if (view_is_displayed(view
)) {
834 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
835 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
838 open_view(struct view
*prev
, struct view
*view
, enum open_flags flags
)
840 bool reload
= !!(flags
& (OPEN_RELOAD
| OPEN_PREPARED
));
841 int nviews
= displayed_views();
843 assert(flags
^ OPEN_REFRESH
);
845 if (view
== prev
&& nviews
== 1 && !reload
) {
846 report("Already in %s view", view
->name
);
850 if (!view_has_flags(view
, VIEW_NO_GIT_DIR
) && !repo
.git_dir
[0]) {
851 report("The %s view is disabled in pager mode", view
->name
);
856 view
->keymap
= get_keymap(view
->name
, strlen(view
->name
));
857 load_view(view
, prev
? prev
: view
, flags
);
861 open_argv(struct view
*prev
, struct view
*view
, const char *argv
[], const char *dir
, enum open_flags flags
)
864 end_update(view
, TRUE
);
867 if (!argv_copy(&view
->argv
, argv
)) {
868 report("Failed to open %s view: %s", view
->name
, io_strerror(&view
->io
));
870 open_view(prev
, view
, flags
| OPEN_PREPARED
);
878 static struct view
*sorting_view
;
880 #define apply_comparator(cmp, o1, o2) \
881 (!(o1) || !(o2)) ? !!(o2) - !!(o1) : cmp(o1, o2)
883 #define number_compare(size1, size2) (*(size1) - *(size2))
885 #define mode_is_dir(mode) ((mode) && S_ISDIR(*(mode)))
888 compare_view_column(enum view_column_type column
, bool use_file_mode
,
889 const struct line
*line1
, struct view_column_data
*column_data1
,
890 const struct line
*line2
, struct view_column_data
*column_data2
)
893 case VIEW_COLUMN_AUTHOR
:
894 return apply_comparator(ident_compare
, column_data1
->author
, column_data2
->author
);
896 case VIEW_COLUMN_DATE
:
897 return apply_comparator(timecmp
, column_data1
->date
, column_data2
->date
);
900 if (column_data1
->reflog
&& column_data2
->reflog
)
901 return apply_comparator(strcmp
, column_data1
->reflog
, column_data2
->reflog
);
902 return apply_comparator(strcmp
, column_data1
->id
, column_data2
->id
);
904 case VIEW_COLUMN_FILE_NAME
:
905 if (use_file_mode
&& mode_is_dir(column_data1
->mode
) != mode_is_dir(column_data2
->mode
))
906 return mode_is_dir(column_data1
->mode
) ? -1 : 1;
907 return apply_comparator(strcmp
, column_data1
->file_name
, column_data2
->file_name
);
909 case VIEW_COLUMN_FILE_SIZE
:
910 return apply_comparator(number_compare
, column_data1
->file_size
, column_data2
->file_size
);
912 case VIEW_COLUMN_LINE_NUMBER
:
913 return line1
->lineno
- line2
->lineno
;
915 case VIEW_COLUMN_MODE
:
916 return apply_comparator(number_compare
, column_data1
->mode
, column_data2
->mode
);
918 case VIEW_COLUMN_REF
:
919 return apply_comparator(ref_compare
, column_data1
->ref
, column_data2
->ref
);
921 case VIEW_COLUMN_COMMIT_TITLE
:
922 return apply_comparator(strcmp
, column_data1
->commit_title
, column_data2
->commit_title
);
924 case VIEW_COLUMN_SECTION
:
925 return apply_comparator(strcmp
, column_data1
->section
->opt
.section
.text
,
926 column_data2
->section
->opt
.section
.text
);
928 case VIEW_COLUMN_STATUS
:
929 return apply_comparator(number_compare
, column_data1
->status
, column_data2
->status
);
931 case VIEW_COLUMN_TEXT
:
932 return apply_comparator(strcmp
, column_data1
->text
, column_data2
->text
);
938 static enum view_column_type view_column_order
[] = {
939 VIEW_COLUMN_FILE_NAME
,
942 VIEW_COLUMN_FILE_SIZE
,
945 VIEW_COLUMN_COMMIT_TITLE
,
946 VIEW_COLUMN_LINE_NUMBER
,
954 sort_view_compare(const void *l1
, const void *l2
)
956 const struct line
*line1
= l1
;
957 const struct line
*line2
= l2
;
958 struct view_column_data column_data1
= {};
959 struct view_column_data column_data2
= {};
960 struct sort_state
*sort
= &sorting_view
->sort
;
961 enum view_column_type column
= get_sort_field(sorting_view
);
965 if (!sorting_view
->ops
->get_column_data(sorting_view
, line1
, &column_data1
))
967 else if (!sorting_view
->ops
->get_column_data(sorting_view
, line2
, &column_data2
))
970 cmp
= compare_view_column(column
, TRUE
, line1
, &column_data1
, line2
, &column_data2
);
972 /* Ensure stable sorting by ordering ordering by the other
973 * columns if the selected column values are equal. */
974 for (i
= 0; !cmp
&& i
< ARRAY_SIZE(view_column_order
); i
++)
975 if (column
!= view_column_order
[i
])
976 cmp
= compare_view_column(view_column_order
[i
], FALSE
,
977 line1
, &column_data1
,
978 line2
, &column_data2
);
980 return sort
->reverse
? -cmp
: cmp
;
984 sort_view(struct view
*view
, bool change_field
)
986 struct sort_state
*state
= &view
->sort
;
990 state
->current
= state
->current
->next
991 ? state
->current
->next
: view
->columns
;
992 if (get_sort_field(view
) == VIEW_COLUMN_ID
&&
993 !state
->current
->opt
.id
.display
)
998 state
->reverse
= !state
->reverse
;
1001 sorting_view
= view
;
1002 qsort(view
->line
, view
->lines
, sizeof(*view
->line
), sort_view_compare
);
1006 view_column_text(struct view
*view
, struct view_column_data
*column_data
,
1007 struct view_column
*column
)
1009 const char *text
= "";
1011 switch (column
->type
) {
1012 case VIEW_COLUMN_AUTHOR
:
1013 if (column_data
->author
)
1014 text
= mkauthor(column_data
->author
, column
->opt
.author
.width
, column
->opt
.author
.display
);
1017 case VIEW_COLUMN_COMMIT_TITLE
:
1018 text
= column_data
->commit_title
;
1021 case VIEW_COLUMN_DATE
:
1022 if (column_data
->date
)
1023 text
= mkdate(column_data
->date
, column
->opt
.date
.display
);
1026 case VIEW_COLUMN_REF
:
1027 if (column_data
->ref
)
1028 text
= column_data
->ref
->name
;
1031 case VIEW_COLUMN_FILE_NAME
:
1032 if (column_data
->file_name
)
1033 text
= column_data
->file_name
;
1036 case VIEW_COLUMN_FILE_SIZE
:
1037 if (column_data
->file_size
)
1038 text
= mkfilesize(*column_data
->file_size
, column
->opt
.file_size
.display
);
1041 case VIEW_COLUMN_ID
:
1042 if (column
->opt
.id
.display
)
1043 text
= column_data
->reflog
? column_data
->reflog
: column_data
->id
;
1046 case VIEW_COLUMN_LINE_NUMBER
:
1049 case VIEW_COLUMN_MODE
:
1050 if (column_data
->mode
)
1051 text
= mkmode(*column_data
->mode
);
1054 case VIEW_COLUMN_STATUS
:
1055 if (column_data
->status
)
1056 text
= mkstatus(*column_data
->status
, column
->opt
.status
.display
);
1059 case VIEW_COLUMN_SECTION
:
1060 text
= column_data
->section
->opt
.section
.text
;
1063 case VIEW_COLUMN_TEXT
:
1064 text
= column_data
->text
;
1068 return text
? text
: "";
1072 grep_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*list
)
1080 for (i
= 0; i
< list
->size
; i
++) {
1081 if (!regexec(view
->regex
, list
->refs
[i
]->name
, 1, &pmatch
, 0))
1089 view_column_grep(struct view
*view
, struct line
*line
)
1091 struct view_column_data column_data
= {};
1092 bool ok
= view
->ops
->get_column_data(view
, line
, &column_data
);
1093 struct view_column
*column
;
1098 for (column
= view
->columns
; column
; column
= column
->next
) {
1099 const char *text
[] = {
1100 view_column_text(view
, &column_data
, column
),
1104 if (grep_text(view
, text
))
1107 if (column
->type
== VIEW_COLUMN_COMMIT_TITLE
&&
1108 column
->opt
.commit_title
.refs
&&
1109 grep_refs(view
, column
, column_data
.refs
))
1117 view_column_info_changed(struct view
*view
, bool update
)
1119 struct view_column
*column
;
1120 bool changed
= FALSE
;
1122 for (column
= view
->columns
; column
; column
= column
->next
) {
1123 if (memcmp(&column
->prev_opt
, &column
->opt
, sizeof(column
->opt
))) {
1126 column
->prev_opt
= column
->opt
;
1135 view_column_reset(struct view
*view
)
1137 struct view_column
*column
;
1139 view_column_info_changed(view
, TRUE
);
1140 for (column
= view
->columns
; column
; column
= column
->next
)
1144 static enum status_code
1145 parse_view_column_config(char **pos
, const char **name
, const char **value
, bool first
)
1147 size_t len
= strcspn(*pos
, ",");
1150 if (strlen(*pos
) > len
)
1152 optlen
= strcspn(*pos
, ":=");
1157 if (optlen
== len
) {
1158 *value
= len
? *pos
: "yes";
1163 /* Fake boolean enum value. */
1172 *value
= *pos
+ optlen
+ 1;
1179 static enum status_code
1180 parse_view_column_option(struct view_column
*column
,
1181 const char *opt_name
, const char *opt_value
)
1183 #define DEFINE_COLUMN_OPTION_INFO(name, type, flags) \
1184 { #name, STRING_SIZE(#name), #type, &opt->name, flags },
1186 #define DEFINE_COLUMN_OPTIONS_PARSE(name, id, options) \
1187 if (column->type == VIEW_COLUMN_##id) { \
1188 struct name##_options *opt = &column->opt.name; \
1189 struct option_info info[] = { \
1190 options(DEFINE_COLUMN_OPTION_INFO) \
1192 struct option_info *option = find_option_info(info, ARRAY_SIZE(info), "", opt_name); \
1194 return error("Unknown option `%s' for column %s", opt_name, \
1195 view_column_name(VIEW_COLUMN_##id)); \
1196 return parse_option(option, #name, opt_value); \
1199 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_PARSE
);
1201 return error("Unknown view column option: %s", opt_name
);
1204 static enum status_code
1205 parse_view_column_type(struct view_column
*column
, const char **arg
)
1207 enum view_column_type type
;
1208 size_t typelen
= strcspn(*arg
, ":,");
1210 for (type
= 0; type
< view_column_type_map
->size
; type
++)
1211 if (enum_equals(view_column_type_map
->entries
[type
], *arg
, typelen
)) {
1212 *arg
+= typelen
+ !!(*arg
)[typelen
];
1213 column
->type
= type
;
1217 return error("Failed to parse view column type: %.*s", (int) typelen
, *arg
);
1220 static struct view
*
1221 find_view(const char *view_name
)
1226 foreach_view(view
, i
)
1227 if (!strncmp(view_name
, view
->name
, strlen(view
->name
)))
1234 parse_view_config(const char *view_name
, const char *argv
[])
1236 enum status_code code
= SUCCESS
;
1237 size_t size
= argv_size(argv
);
1238 struct view_column
*columns
;
1239 struct view_column
*column
;
1240 struct view
*view
= find_view(view_name
);
1244 return error("Unknown view: %s", view_name
);
1246 columns
= calloc(size
, sizeof(*columns
));
1248 return ERROR_OUT_OF_MEMORY
;
1250 for (i
= 0, column
= NULL
; code
== SUCCESS
&& i
< size
; i
++) {
1251 const char *arg
= argv
[i
];
1252 char buf
[SIZEOF_STR
] = "";
1257 column
->next
= &columns
[i
];
1258 column
= &columns
[i
];
1260 code
= parse_view_column_type(column
, &arg
);
1261 if (code
!= SUCCESS
)
1264 if (!(view
->ops
->column_bits
& (1 << column
->type
)))
1265 return error("The %s view does not support %s column", view
->name
,
1266 view_column_name(column
->type
));
1268 if ((column
->type
== VIEW_COLUMN_TEXT
||
1269 column
->type
== VIEW_COLUMN_COMMIT_TITLE
) &&
1271 return error("The %s column must always be last",
1272 view_column_name(column
->type
));
1274 string_ncopy(buf
, arg
, strlen(arg
));
1276 for (pos
= buf
, end
= pos
+ strlen(pos
); code
== SUCCESS
&& pos
<= end
; first
= FALSE
) {
1277 const char *name
= NULL
;
1278 const char *value
= NULL
;
1280 code
= parse_view_column_config(&pos
, &name
, &value
, first
);
1281 if (code
== SUCCESS
)
1282 code
= parse_view_column_option(column
, name
, value
);
1285 column
->prev_opt
= column
->opt
;
1288 if (code
== SUCCESS
) {
1289 free(view
->columns
);
1290 view
->columns
= columns
;
1291 view
->sort
.current
= view
->columns
;
1299 struct view_column
*
1300 get_view_column(struct view
*view
, enum view_column_type type
)
1302 struct view_column
*column
;
1304 for (column
= view
->columns
; column
; column
= column
->next
)
1305 if (column
->type
== type
)
1311 view_column_info_update(struct view
*view
, struct line
*line
)
1313 struct view_column_data column_data
= {};
1314 struct view_column
*column
;
1315 bool changed
= FALSE
;
1317 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
1320 for (column
= view
->columns
; column
; column
= column
->next
) {
1321 const char *text
= view_column_text(view
, &column_data
, column
);
1324 switch (column
->type
) {
1325 case VIEW_COLUMN_AUTHOR
:
1326 width
= column
->opt
.author
.width
;
1329 case VIEW_COLUMN_COMMIT_TITLE
:
1330 width
= column
->opt
.commit_title
.width
;
1333 case VIEW_COLUMN_DATE
:
1334 width
= column
->opt
.date
.width
;
1337 case VIEW_COLUMN_FILE_NAME
:
1338 width
= column
->opt
.file_name
.width
;
1341 case VIEW_COLUMN_FILE_SIZE
:
1342 width
= column
->opt
.file_size
.width
;
1345 case VIEW_COLUMN_ID
:
1346 width
= column
->opt
.id
.width
;
1348 width
= opt_id_width
;
1349 if (!column_data
.reflog
&& !width
)
1353 case VIEW_COLUMN_LINE_NUMBER
:
1354 if (column_data
.line_number
)
1355 width
= count_digits(*column_data
.line_number
);
1357 width
= count_digits(view
->lines
);
1362 case VIEW_COLUMN_MODE
:
1363 width
= column
->opt
.mode
.width
;
1366 case VIEW_COLUMN_REF
:
1367 width
= column
->opt
.ref
.width
;
1370 case VIEW_COLUMN_SECTION
:
1373 case VIEW_COLUMN_STATUS
:
1374 width
= column
->opt
.status
.width
;
1377 case VIEW_COLUMN_TEXT
:
1378 width
= column
->opt
.text
.width
;
1382 if (*text
&& !width
)
1383 width
= utf8_width(text
);
1385 if (width
> column
->width
) {
1386 column
->width
= width
;
1392 view
->force_redraw
= TRUE
;
1397 find_line_by_type(struct view
*view
, struct line
*line
, enum line_type type
, int direction
)
1399 for (; view_has_line(view
, line
); line
+= direction
)
1400 if (line
->type
== type
)
1410 DEFINE_ALLOCATOR(realloc_lines
, struct line
, 256)
1413 add_line_at(struct view
*view
, unsigned long pos
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1416 unsigned long lineno
;
1418 if (!realloc_lines(&view
->line
, view
->lines
, 1))
1422 void *alloc_data
= calloc(1, data_size
);
1428 memcpy(alloc_data
, data
, data_size
);
1432 if (pos
< view
->lines
) {
1434 line
= view
->line
+ pos
;
1435 lineno
= line
->lineno
;
1437 memmove(line
+ 1, line
, (view
->lines
- pos
) * sizeof(*view
->line
));
1438 while (pos
< view
->lines
) {
1439 view
->line
[pos
].lineno
++;
1440 view
->line
[pos
++].dirty
= 1;
1443 line
= &view
->line
[view
->lines
++];
1444 lineno
= view
->lines
- view
->custom_lines
;
1447 memset(line
, 0, sizeof(*line
));
1449 line
->data
= (void *) data
;
1453 view
->custom_lines
++;
1455 line
->lineno
= lineno
;
1461 add_line(struct view
*view
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1463 return add_line_at(view
, view
->lines
, data
, type
, data_size
, custom
);
1467 add_line_alloc_(struct view
*view
, void **ptr
, enum line_type type
, size_t data_size
, bool custom
)
1469 struct line
*line
= add_line(view
, NULL
, type
, data_size
, custom
);
1477 add_line_nodata(struct view
*view
, enum line_type type
)
1479 return add_line(view
, NULL
, type
, 0, FALSE
);
1483 add_line_text(struct view
*view
, const char *text
, enum line_type type
)
1485 struct line
*line
= add_line(view
, text
, type
, strlen(text
) + 1, FALSE
);
1487 if (line
&& view
->ops
->column_bits
)
1488 view_column_info_update(view
, line
);
1492 struct line
* PRINTF_LIKE(3, 4)
1493 add_line_format(struct view
*view
, enum line_type type
, const char *fmt
, ...)
1495 char buf
[SIZEOF_STR
];
1498 FORMAT_BUFFER(buf
, sizeof(buf
), fmt
, retval
, FALSE
);
1499 return retval
>= 0 ? add_line_text(view
, buf
, type
) : NULL
;
1503 * Global view state.
1506 /* Included last to not pollute the rest of the file. */
1507 #include "tig/main.h"
1508 #include "tig/diff.h"
1509 #include "tig/log.h"
1510 #include "tig/tree.h"
1511 #include "tig/blob.h"
1512 #include "tig/blame.h"
1513 #include "tig/refs.h"
1514 #include "tig/status.h"
1515 #include "tig/stage.h"
1516 #include "tig/stash.h"
1517 #include "tig/grep.h"
1518 #include "tig/pager.h"
1519 #include "tig/help.h"
1521 static struct view
*views
[] = {
1522 #define VIEW_DATA(id, name) &name##_view
1523 VIEW_INFO(VIEW_DATA
)
1529 return 0 <= i
&& i
< ARRAY_SIZE(views
) ? views
[i
] : NULL
;
1532 /* vim: set ts=8 sw=8 noexpandtab: */