1 /* Copyright (c) 2006-2015 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"
20 #include "tig/search.h"
22 #include "tig/display.h"
29 goto_view_line(struct view
*view
, unsigned long offset
, unsigned long lineno
)
31 if (lineno
>= view
->lines
)
32 lineno
= view
->lines
> 0 ? view
->lines
- 1 : 0;
34 if (offset
> lineno
|| offset
+ view
->height
<= lineno
) {
35 unsigned long half
= view
->height
/ 2;
38 offset
= lineno
- half
;
43 if (offset
!= view
->pos
.offset
|| lineno
!= view
->pos
.lineno
) {
44 view
->pos
.offset
= offset
;
45 view
->pos
.lineno
= lineno
;
52 /* Scrolling backend */
54 do_scroll_view(struct view
*view
, int lines
)
56 bool redraw_current_line
= false;
58 /* The rendering expects the new offset. */
59 view
->pos
.offset
+= lines
;
61 assert(0 <= view
->pos
.offset
&& view
->pos
.offset
< view
->lines
);
64 /* Move current line into the view. */
65 if (view
->pos
.lineno
< view
->pos
.offset
) {
66 view
->pos
.lineno
= view
->pos
.offset
;
67 redraw_current_line
= true;
68 } else if (view
->pos
.lineno
>= view
->pos
.offset
+ view
->height
) {
69 view
->pos
.lineno
= view
->pos
.offset
+ view
->height
- 1;
70 redraw_current_line
= true;
73 assert(view
->pos
.offset
<= view
->pos
.lineno
&& view
->pos
.lineno
< view
->lines
);
75 /* Redraw the whole screen if scrolling is pointless. */
76 if (view
->height
< ABS(lines
)) {
80 int line
= lines
> 0 ? view
->height
- lines
: 0;
81 int end
= line
+ ABS(lines
);
83 scrollok(view
->win
, true);
84 wscrl(view
->win
, lines
);
85 scrollok(view
->win
, false);
87 while (line
< end
&& draw_view_line(view
, line
))
90 if (redraw_current_line
)
91 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
92 wnoutrefresh(view
->win
);
95 view
->has_scrolled
= true;
101 scroll_view(struct view
*view
, enum request request
)
105 assert(view_is_displayed(view
));
107 if (request
== REQ_SCROLL_WHEEL_DOWN
|| request
== REQ_SCROLL_WHEEL_UP
)
108 lines
= opt_mouse_scroll
;
111 case REQ_SCROLL_FIRST_COL
:
113 redraw_view_from(view
, 0);
116 case REQ_SCROLL_LEFT
:
117 if (view
->pos
.col
== 0) {
118 report("Cannot scroll beyond the first column");
121 if (view
->pos
.col
<= apply_step(opt_horizontal_scroll
, view
->width
))
124 view
->pos
.col
-= apply_step(opt_horizontal_scroll
, view
->width
);
125 redraw_view_from(view
, 0);
128 case REQ_SCROLL_RIGHT
:
129 view
->pos
.col
+= apply_step(opt_horizontal_scroll
, view
->width
);
133 case REQ_SCROLL_PAGE_DOWN
:
134 lines
= view
->height
;
135 case REQ_SCROLL_WHEEL_DOWN
:
136 case REQ_SCROLL_LINE_DOWN
:
137 if (view
->pos
.offset
+ lines
> view
->lines
)
138 lines
= view
->lines
- view
->pos
.offset
;
140 if (lines
== 0 || view
->pos
.offset
+ view
->height
>= view
->lines
) {
141 report("Cannot scroll beyond the last line");
146 case REQ_SCROLL_PAGE_UP
:
147 lines
= view
->height
;
148 case REQ_SCROLL_LINE_UP
:
149 case REQ_SCROLL_WHEEL_UP
:
150 if (lines
> view
->pos
.offset
)
151 lines
= view
->pos
.offset
;
154 report("Cannot scroll beyond the first line");
162 die("request %d not handled in switch", request
);
165 do_scroll_view(view
, lines
);
170 move_view(struct view
*view
, enum request request
)
172 int scroll_steps
= 0;
176 case REQ_MOVE_FIRST_LINE
:
177 steps
= -view
->pos
.lineno
;
180 case REQ_MOVE_LAST_LINE
:
181 steps
= view
->lines
- view
->pos
.lineno
- 1;
184 case REQ_MOVE_PAGE_UP
:
185 steps
= view
->height
> view
->pos
.lineno
186 ? -view
->pos
.lineno
: -view
->height
;
189 case REQ_MOVE_PAGE_DOWN
:
190 steps
= view
->pos
.lineno
+ view
->height
>= view
->lines
191 ? view
->lines
- view
->pos
.lineno
- 1 : view
->height
;
194 case REQ_MOVE_HALF_PAGE_UP
:
195 steps
= view
->height
/ 2 > view
->pos
.lineno
196 ? -view
->pos
.lineno
: -(view
->height
/ 2);
199 case REQ_MOVE_HALF_PAGE_DOWN
:
200 steps
= view
->pos
.lineno
+ view
->height
/ 2 >= view
->lines
201 ? view
->lines
- view
->pos
.lineno
- 1 : view
->height
/ 2;
215 die("request %d not handled in switch", request
);
218 if (steps
<= 0 && view
->pos
.lineno
== 0) {
219 report("Cannot move beyond the first line");
222 } else if (steps
>= 0 && view
->pos
.lineno
+ 1 >= view
->lines
) {
223 report("Cannot move beyond the last line");
227 /* Move the current line */
228 view
->pos
.lineno
+= steps
;
229 assert(0 <= view
->pos
.lineno
&& view
->pos
.lineno
< view
->lines
);
231 /* Check whether the view needs to be scrolled */
232 if (view
->pos
.lineno
< view
->pos
.offset
||
233 view
->pos
.lineno
>= view
->pos
.offset
+ view
->height
) {
234 scroll_steps
= steps
;
235 if (steps
< 0 && -steps
> view
->pos
.offset
) {
236 scroll_steps
= -view
->pos
.offset
;
238 } else if (steps
> 0) {
239 if (view
->pos
.lineno
== view
->lines
- 1 &&
240 view
->lines
> view
->height
) {
241 scroll_steps
= view
->lines
- view
->pos
.offset
- 1;
242 if (scroll_steps
>= view
->height
)
243 scroll_steps
-= view
->height
- 1;
248 if (!view_is_displayed(view
)) {
249 view
->pos
.offset
+= scroll_steps
;
250 assert(0 <= view
->pos
.offset
&& view
->pos
.offset
< view
->lines
);
251 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
255 /* Repaint the old "current" line if we be scrolling */
256 if (ABS(steps
) < view
->height
)
257 draw_view_line(view
, view
->pos
.lineno
- steps
- view
->pos
.offset
);
260 do_scroll_view(view
, scroll_steps
);
264 /* Draw the current line */
265 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
267 wnoutrefresh(view
->win
);
272 select_view_line(struct view
*view
, unsigned long lineno
)
274 struct position old
= view
->pos
;
276 if (goto_view_line(view
, view
->pos
.offset
, lineno
)) {
277 if (view_is_displayed(view
)) {
278 if (old
.offset
!= view
->pos
.offset
) {
281 draw_view_line(view
, old
.lineno
- view
->pos
.offset
);
282 draw_view_line(view
, view
->pos
.lineno
- view
->pos
.offset
);
283 wnoutrefresh(view
->win
);
286 view
->ops
->select(view
, &view
->line
[view
->pos
.lineno
]);
292 goto_id(struct view
*view
, const char *expr
, bool from_start
, bool save_search
)
294 struct view_column_data column_data
= {0};
295 char id
[SIZEOF_STR
] = "";
297 struct line
*line
= &view
->line
[view
->pos
.lineno
];
299 if (!(view
->ops
->column_bits
& view_column_bit(ID
))) {
300 report("Jumping to ID is not supported by the %s view", view
->name
);
303 char *rev
= argv_format_arg(view
->env
, expr
);
304 const char *rev_parse_argv
[] = {
305 "git", "rev-parse", "--revs-only", rev
, NULL
307 bool ok
= rev
&& io_run_buf(rev_parse_argv
, id
, sizeof(id
), true);
311 report("Failed to parse expression '%s'", expr
);
317 if (view
->ops
->get_column_data(view
, line
, &column_data
)
318 && column_data
.id
&& string_rev_is_null(column_data
.id
)) {
319 select_view_line(view
, view
->pos
.lineno
+ 1);
322 report("Expression '%s' is not a meaningful revision", expr
);
327 line
= from_start
? view
->line
: &view
->line
[view
->pos
.lineno
];
329 for (idlen
= strlen(id
); view_has_line(view
, line
); line
++) {
330 struct view_column_data column_data
= {0};
332 if (view
->ops
->get_column_data(view
, line
, &column_data
) &&
334 !strncasecmp(column_data
.id
, id
, idlen
)) {
336 string_ncopy(view
->env
->search
, id
, idlen
);
337 select_view_line(view
, line
- view
->line
);
343 report("Unable to find commit '%s'", view
->env
->search
);
351 view_history_is_empty(struct view_history
*history
)
353 return !history
->stack
;
357 push_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
359 struct view_state
*state
= history
->stack
;
361 if (state
&& data
&& history
->state_alloc
&&
362 !memcmp(state
->data
, data
, history
->state_alloc
))
365 state
= calloc(1, sizeof(*state
) + history
->state_alloc
);
369 state
->prev
= history
->stack
;
370 history
->stack
= state
;
371 clear_position(&history
->position
);
372 state
->position
= *position
;
373 state
->data
= &state
[1];
374 if (data
&& history
->state_alloc
)
375 memcpy(state
->data
, data
, history
->state_alloc
);
380 pop_view_history_state(struct view_history
*history
, struct position
*position
, void *data
)
382 struct view_state
*state
= history
->stack
;
384 if (view_history_is_empty(history
))
387 history
->position
= state
->position
;
388 history
->stack
= state
->prev
;
390 if (data
&& history
->state_alloc
)
391 memcpy(data
, state
->data
, history
->state_alloc
);
393 *position
= state
->position
;
400 reset_view_history(struct view_history
*history
)
402 while (pop_view_history_state(history
, NULL
, NULL
))
407 * Incremental updating
411 reset_view(struct view
*view
)
415 for (i
= 0; i
< view
->lines
; i
++)
416 free(view
->line
[i
].data
);
420 view
->prev_pos
= view
->pos
;
421 /* A view without a previous view is the first view */
422 if (!view
->prev
&& !view
->lines
&& view
->prev_pos
.lineno
== 0)
423 view
->prev_pos
.lineno
= view
->env
->goto_lineno
;
424 clear_position(&view
->pos
);
427 view_column_reset(view
);
432 view
->custom_lines
= 0;
433 view
->update_secs
= 0;
437 restore_view_position(struct view
*view
)
439 /* Ensure that the view position is in a valid state. */
440 if (!check_position(&view
->prev_pos
) ||
441 (view
->pipe
&& view
->lines
<= view
->prev_pos
.lineno
))
442 return goto_view_line(view
, view
->pos
.offset
, view
->pos
.lineno
);
444 /* Changing the view position cancels the restoring. */
445 /* FIXME: Changing back to the first line is not detected. */
446 if (check_position(&view
->pos
)) {
447 clear_position(&view
->prev_pos
);
451 if (goto_view_line(view
, view
->prev_pos
.offset
, view
->prev_pos
.lineno
) &&
452 view_is_displayed(view
))
455 view
->pos
.col
= view
->prev_pos
.col
;
456 clear_position(&view
->prev_pos
);
462 end_update(struct view
*view
, bool force
)
466 while (!view
->ops
->read(view
, NULL
, force
))
476 setup_update(struct view
*view
, const char *vid
)
479 /* XXX: Do not use string_copy_rev(), it copies until first space. */
480 string_ncopy(view
->vid
, vid
, strlen(vid
));
481 view
->pipe
= &view
->io
;
482 view
->start_time
= time(NULL
);
486 view_no_refresh(struct view
*view
, enum open_flags flags
)
488 bool reload
= !!(flags
& OPEN_ALWAYS_LOAD
) || !view
->lines
;
490 return (!reload
&& !strcmp(view
->vid
, view
->ops
->id
)) ||
491 ((flags
& OPEN_REFRESH
) && !view_can_refresh(view
));
495 view_exec(struct view
*view
, enum open_flags flags
)
497 char opt_env_lines
[64] = "";
498 char opt_env_columns
[64] = "";
499 char * const opt_env
[] = { opt_env_lines
, opt_env_columns
, NULL
};
501 enum io_flags forward_stdin
= (flags
& OPEN_FORWARD_STDIN
) ? IO_RD_FORWARD_STDIN
: 0;
502 enum io_flags with_stderr
= (flags
& OPEN_WITH_STDERR
) ? IO_RD_WITH_STDERR
: 0;
503 enum io_flags io_flags
= forward_stdin
| with_stderr
;
505 int views
= displayed_views();
506 bool split
= (views
== 1 && !!(flags
& OPEN_SPLIT
)) || views
== 2;
509 getmaxyx(stdscr
, height
, width
);
510 if (split
&& vertical_split_is_enabled(opt_vertical_split
, height
, width
)) {
511 bool is_base_view
= display
[0] == view
;
512 int split_width
= apply_vertical_split(width
);
515 width
-= split_width
;
517 width
= split_width
- 1;
520 string_format(opt_env_columns
, "COLUMNS=%d", MAX(0, width
));
521 string_format(opt_env_lines
, "LINES=%d", height
);
523 return io_exec(&view
->io
, IO_RD
, view
->dir
, opt_env
, view
->argv
, io_flags
);
527 begin_update(struct view
*view
, const char *dir
, const char **argv
, enum open_flags flags
)
529 bool extra
= !!(flags
& (OPEN_EXTRA
));
530 bool refresh
= flags
& (OPEN_REFRESH
| OPEN_PREPARED
| OPEN_STDIN
);
532 if (view_no_refresh(view
, flags
))
539 end_update(view
, true);
542 view
->unrefreshable
= open_in_pager_mode(flags
);
544 if (!refresh
&& argv
) {
545 bool file_filter
= !view_has_flags(view
, VIEW_FILE_FILTER
) || opt_file_filter
;
548 if (!argv_format(view
->env
, &view
->argv
, argv
, !view
->prev
, file_filter
)) {
549 report("Failed to format %s arguments", view
->name
);
554 if (view
->argv
&& view
->argv
[0] &&
555 !view_exec(view
, flags
)) {
556 report("Failed to open %s view", view
->name
);
560 if (open_from_stdin(flags
)) {
561 if (!io_open(&view
->io
, "%s", ""))
562 die("Failed to open stdin");
566 setup_update(view
, view
->ops
->id
);
572 update_view(struct view
*view
)
574 /* Clear the view and redraw everything since the tree sorting
575 * might have rearranged things. */
576 bool redraw
= view
->lines
== 0;
577 bool can_read
= true;
578 struct encoding
*encoding
= view
->encoding
? view
->encoding
: default_encoding
;
584 if (!io_can_read(view
->pipe
, false)) {
585 if (view
->lines
== 0 && view_is_displayed(view
)) {
586 time_t secs
= time(NULL
) - view
->start_time
;
588 if (secs
> 1 && secs
> view
->update_secs
) {
589 if (view
->update_secs
== 0)
591 update_view_title(view
);
592 view
->update_secs
= secs
;
598 for (; io_get(view
->pipe
, &line
, '\n', can_read
); can_read
= false) {
599 if (encoding
&& !encoding_convert(encoding
, &line
)) {
600 report("Encoding failure");
601 end_update(view
, true);
605 if (!view
->ops
->read(view
, &line
, false)) {
606 report("Allocation failure");
607 end_update(view
, true);
612 if (io_error(view
->pipe
)) {
613 report("Failed to read: %s", io_strerror(view
->pipe
));
614 end_update(view
, true);
616 } else if (io_eof(view
->pipe
)) {
617 end_update(view
, false);
620 if (restore_view_position(view
))
623 if (!view_is_displayed(view
))
626 if (redraw
|| view
->force_redraw
)
627 redraw_view_from(view
, 0);
629 redraw_view_dirty(view
);
630 view
->force_redraw
= false;
632 /* Update the title _after_ the redraw so that if the redraw picks up a
633 * commit reference in view->ref it'll be available here. */
634 update_view_title(view
);
639 update_view_title(struct view
*view
)
641 WINDOW
*window
= view
->title
;
642 struct line
*line
= &view
->line
[view
->pos
.lineno
];
643 unsigned int view_lines
, lines
;
645 assert(view_is_displayed(view
));
647 if (view
== display
[current_view
])
648 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_FOCUS
));
650 wbkgdset(window
, get_view_attr(view
, LINE_TITLE_BLUR
));
653 mvwprintw(window
, 0, 0, "[%s]", view
->name
);
656 wprintw(window
, " %s", view
->ref
);
659 if (!view_has_flags(view
, VIEW_CUSTOM_STATUS
) && view_has_line(view
, line
) &&
661 wprintw(window
, " - %s %d of %zd",
664 view
->lines
- view
->custom_lines
);
668 time_t secs
= time(NULL
) - view
->start_time
;
670 /* Three git seconds are a long time ... */
672 wprintw(window
, " loading %lds", secs
);
675 view_lines
= view
->pos
.offset
+ view
->height
;
676 lines
= view
->lines
? MIN(view_lines
, view
->lines
) * 100 / view
->lines
: 0;
677 mvwprintw(window
, 0, view
->width
- count_digits(lines
) - 1, "%d%%", lines
);
679 wnoutrefresh(window
);
687 split_view(struct view
*prev
, struct view
*view
)
690 current_view
= opt_focus_child
? 1 : 0;
694 if (prev
->pos
.lineno
- prev
->pos
.offset
>= prev
->height
) {
695 /* Take the title line into account. */
696 int lines
= prev
->pos
.lineno
- prev
->pos
.offset
- prev
->height
+ 1;
698 /* Scroll the view that was split if the current line is
699 * outside the new limited view. */
700 do_scroll_view(prev
, lines
);
703 if (view
!= prev
&& view_is_displayed(prev
)) {
704 /* "Blur" the previous view. */
705 update_view_title(prev
);
708 if (view_has_flags(prev
, VIEW_FLEX_WIDTH
))
709 load_view(prev
, NULL
, OPEN_RELOAD
);
713 maximize_view(struct view
*view
, bool redraw
)
715 memset(display
, 0, sizeof(display
));
717 display
[current_view
] = view
;
720 redraw_display(false);
724 if (view_has_flags(view
, VIEW_FLEX_WIDTH
))
725 load_view(view
, NULL
, OPEN_RELOAD
);
729 load_view(struct view
*view
, struct view
*prev
, enum open_flags flags
)
731 bool refresh
= !view_no_refresh(view
, flags
);
733 /* When prev == view it means this is the first loaded view. */
734 if (prev
&& view
!= prev
) {
738 if (!refresh
&& view_can_refresh(view
) &&
739 watch_update_single(&view
->watch
, WATCH_EVENT_SWITCH_VIEW
)) {
740 refresh
= watch_dirty(&view
->watch
);
742 flags
|= OPEN_REFRESH
;
747 end_update(view
, true);
748 if (view
->ops
->private_size
) {
749 if (!view
->private) {
750 view
->private = calloc(1, view
->ops
->private_size
);
753 view
->ops
->done(view
);
754 memset(view
->private, 0, view
->ops
->private_size
);
758 if (!view
->ops
->open(view
, flags
))
763 bool split
= !!(flags
& OPEN_SPLIT
);
766 split_view(prev
, view
);
768 maximize_view(view
, false);
772 restore_view_position(view
);
774 if (view
->pipe
&& view
->lines
== 0) {
775 /* Clear the old view and let the incremental updating refill
778 /* Do not clear the position if it is the first view. */
779 if (view
->prev
&& !(flags
& (OPEN_RELOAD
| OPEN_REFRESH
)))
780 clear_position(&view
->prev_pos
);
782 } else if (view_is_displayed(view
)) {
788 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
789 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
792 open_view(struct view
*prev
, struct view
*view
, enum open_flags flags
)
794 bool reload
= !!(flags
& (OPEN_RELOAD
| OPEN_PREPARED
));
795 int nviews
= displayed_views();
797 assert(flags
^ OPEN_REFRESH
);
799 if (view
== prev
&& nviews
== 1 && !reload
) {
800 report("Already in %s view", view
->name
);
804 if (!view_has_flags(view
, VIEW_NO_GIT_DIR
) && !repo
.git_dir
[0]) {
805 report("The %s view is disabled in pager mode", view
->name
);
810 view
->keymap
= get_keymap(view
->name
, strlen(view
->name
));
811 load_view(view
, prev
? prev
: view
, flags
);
815 open_argv(struct view
*prev
, struct view
*view
, const char *argv
[], const char *dir
, enum open_flags flags
)
818 end_update(view
, true);
821 if (!argv_copy(&view
->argv
, argv
)) {
822 report("Failed to open %s view: %s", view
->name
, io_strerror(&view
->io
));
824 open_view(prev
, view
, flags
| OPEN_PREPARED
);
832 static struct view
*sorting_view
;
834 #define apply_comparator(cmp, o1, o2) \
835 (!(o1) || !(o2)) ? !!(o2) - !!(o1) : cmp(o1, o2)
837 #define number_compare(size1, size2) (*(size1) - *(size2))
839 #define mode_is_dir(mode) ((mode) && S_ISDIR(*(mode)))
842 compare_view_column(enum view_column_type column
, bool use_file_mode
,
843 const struct line
*line1
, struct view_column_data
*column_data1
,
844 const struct line
*line2
, struct view_column_data
*column_data2
)
847 case VIEW_COLUMN_AUTHOR
:
848 return apply_comparator(ident_compare
, column_data1
->author
, column_data2
->author
);
850 case VIEW_COLUMN_DATE
:
851 return apply_comparator(timecmp
, column_data1
->date
, column_data2
->date
);
854 if (column_data1
->reflog
&& column_data2
->reflog
)
855 return apply_comparator(strcmp
, column_data1
->reflog
, column_data2
->reflog
);
856 return apply_comparator(strcmp
, column_data1
->id
, column_data2
->id
);
858 case VIEW_COLUMN_FILE_NAME
:
859 if (use_file_mode
&& mode_is_dir(column_data1
->mode
) != mode_is_dir(column_data2
->mode
))
860 return mode_is_dir(column_data1
->mode
) ? -1 : 1;
861 return apply_comparator(strcmp
, column_data1
->file_name
, column_data2
->file_name
);
863 case VIEW_COLUMN_FILE_SIZE
:
864 return apply_comparator(number_compare
, column_data1
->file_size
, column_data2
->file_size
);
866 case VIEW_COLUMN_LINE_NUMBER
:
867 return line1
->lineno
- line2
->lineno
;
869 case VIEW_COLUMN_MODE
:
870 return apply_comparator(number_compare
, column_data1
->mode
, column_data2
->mode
);
872 case VIEW_COLUMN_REF
:
873 return apply_comparator(ref_compare
, column_data1
->ref
, column_data2
->ref
);
875 case VIEW_COLUMN_COMMIT_TITLE
:
876 return apply_comparator(strcmp
, column_data1
->commit_title
, column_data2
->commit_title
);
878 case VIEW_COLUMN_SECTION
:
879 return apply_comparator(strcmp
, column_data1
->section
->opt
.section
.text
,
880 column_data2
->section
->opt
.section
.text
);
882 case VIEW_COLUMN_STATUS
:
883 return apply_comparator(number_compare
, column_data1
->status
, column_data2
->status
);
885 case VIEW_COLUMN_TEXT
:
886 if (column_data1
->box
&& column_data2
->box
)
887 return apply_comparator(strcmp
, column_data1
->box
->text
,
888 column_data2
->box
->text
);
889 return apply_comparator(strcmp
, column_data1
->text
, column_data2
->text
);
895 static enum view_column_type view_column_order
[] = {
896 VIEW_COLUMN_FILE_NAME
,
899 VIEW_COLUMN_FILE_SIZE
,
902 VIEW_COLUMN_COMMIT_TITLE
,
903 VIEW_COLUMN_LINE_NUMBER
,
911 sort_view_compare(const void *l1
, const void *l2
)
913 const struct line
*line1
= l1
;
914 const struct line
*line2
= l2
;
915 struct view_column_data column_data1
= {0};
916 struct view_column_data column_data2
= {0};
917 struct sort_state
*sort
= &sorting_view
->sort
;
918 enum view_column_type column
= get_sort_field(sorting_view
);
922 if (!sorting_view
->ops
->get_column_data(sorting_view
, line1
, &column_data1
))
924 else if (!sorting_view
->ops
->get_column_data(sorting_view
, line2
, &column_data2
))
927 cmp
= compare_view_column(column
, true, line1
, &column_data1
, line2
, &column_data2
);
929 /* Ensure stable sorting by ordering by the other
930 * columns if the selected column values are equal. */
931 for (i
= 0; !cmp
&& i
< ARRAY_SIZE(view_column_order
); i
++)
932 if (column
!= view_column_order
[i
])
933 cmp
= compare_view_column(view_column_order
[i
], false,
934 line1
, &column_data1
,
935 line2
, &column_data2
);
937 return sort
->reverse
? -cmp
: cmp
;
941 resort_view(struct view
*view
, bool renumber
)
944 qsort(view
->line
, view
->lines
, sizeof(*view
->line
), sort_view_compare
);
949 for (i
= 0, lineno
= 1; i
< view
->lines
; i
++)
950 if (view
->line
[i
].lineno
)
951 view
->line
[i
].lineno
= lineno
++;
956 sort_view(struct view
*view
, bool change_field
)
958 struct sort_state
*state
= &view
->sort
;
962 state
->current
= state
->current
->next
963 ? state
->current
->next
: view
->columns
;
964 if (get_sort_field(view
) == VIEW_COLUMN_ID
&&
965 !state
->current
->opt
.id
.display
)
970 state
->reverse
= !state
->reverse
;
973 resort_view(view
, false);
977 view_column_text(struct view
*view
, struct view_column_data
*column_data
,
978 struct view_column
*column
)
980 const char *text
= "";
982 switch (column
->type
) {
983 case VIEW_COLUMN_AUTHOR
:
984 if (column_data
->author
)
985 text
= mkauthor(column_data
->author
, column
->opt
.author
.width
, column
->opt
.author
.display
);
988 case VIEW_COLUMN_COMMIT_TITLE
:
989 text
= column_data
->commit_title
;
992 case VIEW_COLUMN_DATE
:
993 if (column_data
->date
)
994 text
= mkdate(column_data
->date
, column
->opt
.date
.display
,
995 column
->opt
.date
.local
, column
->opt
.date
.format
);
998 case VIEW_COLUMN_REF
:
999 if (column_data
->ref
)
1000 text
= column_data
->ref
->name
;
1003 case VIEW_COLUMN_FILE_NAME
:
1004 if (column_data
->file_name
)
1005 text
= column_data
->file_name
;
1008 case VIEW_COLUMN_FILE_SIZE
:
1009 if (column_data
->file_size
)
1010 text
= mkfilesize(*column_data
->file_size
, column
->opt
.file_size
.display
);
1013 case VIEW_COLUMN_ID
:
1014 if (column
->opt
.id
.display
)
1015 text
= column_data
->reflog
? column_data
->reflog
: column_data
->id
;
1018 case VIEW_COLUMN_LINE_NUMBER
:
1021 case VIEW_COLUMN_MODE
:
1022 if (column_data
->mode
)
1023 text
= mkmode(*column_data
->mode
);
1026 case VIEW_COLUMN_STATUS
:
1027 if (column_data
->status
)
1028 text
= mkstatus(*column_data
->status
, column
->opt
.status
.display
);
1031 case VIEW_COLUMN_SECTION
:
1032 text
= column_data
->section
->opt
.section
.text
;
1035 case VIEW_COLUMN_TEXT
:
1036 text
= column_data
->text
;
1040 return text
? text
: "";
1044 grep_refs(struct view
*view
, struct view_column
*column
, const struct ref
*ref
)
1048 for (; ref
; ref
= ref
->next
) {
1049 if (!regexec(view
->regex
, ref
->name
, 1, &pmatch
, 0))
1057 view_column_grep(struct view
*view
, struct line
*line
)
1059 struct view_column_data column_data
= {0};
1060 bool ok
= view
->ops
->get_column_data(view
, line
, &column_data
);
1061 struct view_column
*column
;
1066 for (column
= view
->columns
; column
; column
= column
->next
) {
1067 const char *text
[] = {
1068 view_column_text(view
, &column_data
, column
),
1072 if (grep_text(view
, text
))
1075 if (column
->type
== VIEW_COLUMN_COMMIT_TITLE
&&
1076 column
->opt
.commit_title
.refs
&&
1077 grep_refs(view
, column
, column_data
.refs
))
1085 view_column_info_changed(struct view
*view
, bool update
)
1087 struct view_column
*column
;
1088 bool changed
= false;
1090 for (column
= view
->columns
; column
; column
= column
->next
) {
1091 if (memcmp(&column
->prev_opt
, &column
->opt
, sizeof(column
->opt
))) {
1094 column
->prev_opt
= column
->opt
;
1103 view_column_reset(struct view
*view
)
1105 struct view_column
*column
;
1107 view_column_info_changed(view
, true);
1108 for (column
= view
->columns
; column
; column
= column
->next
)
1112 static enum status_code
1113 parse_view_column_config_expr(char **pos
, const char **name
, const char **value
, bool first
)
1115 size_t len
= strcspn(*pos
, ",");
1118 if (strlen(*pos
) > len
)
1120 optlen
= strcspn(*pos
, ":=");
1125 if (optlen
== len
) {
1126 *value
= len
? *pos
: "yes";
1131 /* Fake boolean enum value. */
1140 *value
= *pos
+ optlen
+ 1;
1147 static enum status_code
1148 parse_view_column_option(struct view_column
*column
,
1149 const char *opt_name
, const char *opt_value
)
1151 #define DEFINE_COLUMN_OPTION_INFO(name, type, flags) \
1152 { #name, STRING_SIZE(#name), #type, &opt->name, flags },
1154 #define DEFINE_COLUMN_OPTIONS_PARSE(name, id, options) \
1155 if (column->type == VIEW_COLUMN_##id) { \
1156 struct name##_options *opt = &column->opt.name; \
1157 struct option_info info[] = { \
1158 options(DEFINE_COLUMN_OPTION_INFO) \
1160 struct option_info *option = find_option_info(info, ARRAY_SIZE(info), "", opt_name); \
1162 return error("Unknown option `%s' for column %s", opt_name, \
1163 view_column_name(VIEW_COLUMN_##id)); \
1164 return parse_option(option, #name, opt_value); \
1167 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_PARSE
);
1169 return error("Unknown view column option: %s", opt_name
);
1172 static enum status_code
1173 parse_view_column_config_exprs(struct view_column
*column
, const char *arg
)
1175 char buf
[SIZEOF_STR
] = "";
1178 enum status_code code
= SUCCESS
;
1180 string_ncopy(buf
, arg
, strlen(arg
));
1182 for (pos
= buf
, end
= pos
+ strlen(pos
); code
== SUCCESS
&& pos
<= end
; first
= false) {
1183 const char *name
= NULL
;
1184 const char *value
= NULL
;
1186 code
= parse_view_column_config_expr(&pos
, &name
, &value
, first
);
1187 if (code
== SUCCESS
)
1188 code
= parse_view_column_option(column
, name
, value
);
1194 static enum status_code
1195 parse_view_column_type(struct view_column
*column
, const char **arg
)
1197 enum view_column_type type
;
1198 size_t typelen
= strcspn(*arg
, ":,");
1200 for (type
= 0; type
< view_column_type_map
->size
; type
++)
1201 if (enum_equals(view_column_type_map
->entries
[type
], *arg
, typelen
)) {
1202 *arg
+= typelen
+ !!(*arg
)[typelen
];
1203 column
->type
= type
;
1207 return error("Failed to parse view column type: %.*s", (int) typelen
, *arg
);
1210 static struct view
*
1211 find_view(const char *view_name
)
1216 foreach_view(view
, i
)
1217 if (!strncmp(view_name
, view
->name
, strlen(view
->name
)))
1224 parse_view_column_config(const char *view_name
, enum view_column_type type
,
1225 const char *option_name
, const char *argv
[])
1227 struct view_column
*column
;
1228 struct view
*view
= find_view(view_name
);
1231 return error("Unknown view: %s", view_name
);
1233 if (!(view
->ops
->column_bits
& (1 << type
)))
1234 return error("The %s view does not support %s column", view
->name
,
1235 view_column_name(type
));
1237 column
= get_view_column(view
, type
);
1239 return error("The %s view does not have a %s column configured", view
->name
,
1240 view_column_name(type
));
1243 return parse_view_column_option(column
, option_name
, argv
[0]);
1244 return parse_view_column_config_exprs(column
, argv
[0]);
1248 parse_view_config(struct view_column
**column_ref
, const char *view_name
, const char *argv
[])
1250 enum status_code code
= SUCCESS
;
1251 size_t size
= argv_size(argv
);
1252 struct view_column
*columns
;
1253 struct view_column
*column
;
1254 struct view
*view
= find_view(view_name
);
1258 return error("Unknown view: %s", view_name
);
1260 columns
= calloc(size
, sizeof(*columns
));
1262 return ERROR_OUT_OF_MEMORY
;
1264 for (i
= 0, column
= NULL
; code
== SUCCESS
&& i
< size
; i
++) {
1265 const char *arg
= argv
[i
];
1268 column
->next
= &columns
[i
];
1269 column
= &columns
[i
];
1271 code
= parse_view_column_type(column
, &arg
);
1272 if (code
!= SUCCESS
)
1275 if (!(view
->ops
->column_bits
& (1 << column
->type
)))
1276 return error("The %s view does not support %s column", view
->name
,
1277 view_column_name(column
->type
));
1279 if ((column
->type
== VIEW_COLUMN_TEXT
||
1280 column
->type
== VIEW_COLUMN_COMMIT_TITLE
) &&
1282 return error("The %s column must always be last",
1283 view_column_name(column
->type
));
1285 code
= parse_view_column_config_exprs(column
, arg
);
1286 column
->prev_opt
= column
->opt
;
1289 if (code
== SUCCESS
) {
1290 free(view
->columns
);
1291 view
->columns
= columns
;
1292 view
->sort
.current
= view
->columns
;
1293 *column_ref
= columns
;
1301 static enum status_code
1302 format_view_column_options(struct option_info options
[], size_t options_size
, char buf
[], size_t bufsize
)
1304 char name
[SIZEOF_STR
];
1305 char value
[SIZEOF_STR
];
1307 const char *sep
= ":";
1312 for (i
= 0; i
< options_size
; i
++) {
1313 struct option_info
*option
= &options
[i
];
1314 const char *assign
= "=";
1316 if (!enum_name_copy(name
, sizeof(name
), option
->name
)
1317 || !format_option_value(option
, value
, sizeof(value
)))
1318 return error("No space left in buffer");
1320 if (!strcmp(name
, "display")) {
1326 if (!strcmp(option
->type
, "bool") && !strcmp(value
, "yes")) {
1332 /* For non-display boolean options 'yes' is implied. */
1339 if (!strcmp(option
->type
, "int") && !strcmp(value
, "0"))
1342 if (!string_nformat(buf
, bufsize
, &bufpos
, "%s%s%s%s",
1343 sep
, name
, assign
, value
))
1344 return error("No space left in buffer");
1352 static enum status_code
1353 format_view_column(struct view_column
*column
, char buf
[], size_t bufsize
)
1355 #define FORMAT_COLUMN_OPTION_INFO(name, type, flags) \
1356 { #name, STRING_SIZE(#name), #type, &opt->name, flags },
1358 #define FORMAT_COLUMN_OPTIONS_PARSE(col_name, id, options) \
1359 if (column->type == VIEW_COLUMN_##id) { \
1360 struct col_name##_options *opt = &column->opt.col_name; \
1361 struct option_info info[] = { \
1362 options(FORMAT_COLUMN_OPTION_INFO) \
1365 return format_view_column_options(info, ARRAY_SIZE(info), buf, bufsize); \
1368 COLUMN_OPTIONS(FORMAT_COLUMN_OPTIONS_PARSE
);
1370 return error("Unknown view column type: %d", column
->type
);
1374 format_view_config(struct view_column
*column
, char buf
[], size_t bufsize
)
1376 const struct enum_map
*map
= view_column_type_map
;
1377 const char *sep
= "";
1379 char type
[SIZEOF_STR
];
1380 char value
[SIZEOF_STR
];
1382 for (; column
; column
= column
->next
) {
1383 enum status_code code
= format_view_column(column
, value
, sizeof(value
));
1385 if (code
!= SUCCESS
)
1388 if (!enum_name_copy(type
, sizeof(type
), map
->entries
[column
->type
].name
)
1389 || !string_nformat(buf
, bufsize
, &bufpos
, "%s%s%s",
1391 return error("No space left in buffer");
1399 struct view_column
*
1400 get_view_column(struct view
*view
, enum view_column_type type
)
1402 struct view_column
*column
;
1404 for (column
= view
->columns
; column
; column
= column
->next
)
1405 if (column
->type
== type
)
1411 view_column_info_update(struct view
*view
, struct line
*line
)
1413 struct view_column_data column_data
= {0};
1414 struct view_column
*column
;
1415 bool changed
= false;
1417 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
1420 for (column
= view
->columns
; column
; column
= column
->next
) {
1421 const char *text
= view_column_text(view
, &column_data
, column
);
1424 switch (column
->type
) {
1425 case VIEW_COLUMN_AUTHOR
:
1426 width
= column
->opt
.author
.width
;
1429 case VIEW_COLUMN_COMMIT_TITLE
:
1430 width
= column
->opt
.commit_title
.width
;
1433 case VIEW_COLUMN_DATE
:
1434 width
= column
->opt
.date
.width
;
1437 case VIEW_COLUMN_FILE_NAME
:
1438 width
= column
->opt
.file_name
.width
;
1441 case VIEW_COLUMN_FILE_SIZE
:
1442 width
= column
->opt
.file_size
.width
;
1445 case VIEW_COLUMN_ID
:
1446 width
= column
->opt
.id
.width
;
1448 width
= opt_id_width
;
1449 if (!column_data
.reflog
&& !width
)
1453 case VIEW_COLUMN_LINE_NUMBER
:
1454 if (column_data
.line_number
)
1455 width
= count_digits(*column_data
.line_number
);
1457 width
= count_digits(view
->lines
);
1462 case VIEW_COLUMN_MODE
:
1463 width
= column
->opt
.mode
.width
;
1466 case VIEW_COLUMN_REF
:
1467 width
= column
->opt
.ref
.width
;
1470 case VIEW_COLUMN_SECTION
:
1473 case VIEW_COLUMN_STATUS
:
1474 width
= column
->opt
.status
.width
;
1477 case VIEW_COLUMN_TEXT
:
1478 width
= column
->opt
.text
.width
;
1482 if (*text
&& !width
)
1483 width
= utf8_width(text
);
1485 if (width
> column
->width
) {
1486 column
->width
= width
;
1492 view
->force_redraw
= true;
1497 find_line_by_type(struct view
*view
, struct line
*line
, enum line_type type
, int direction
)
1499 for (; view_has_line(view
, line
); line
+= direction
)
1500 if (line
->type
== type
)
1510 DEFINE_ALLOCATOR(realloc_lines
, struct line
, 256)
1512 static inline char *
1513 box_text_offset(struct box
*box
, size_t cells
)
1515 return (char *) &box
->cell
[cells
];
1519 box_text_copy(struct box
*box
, size_t cells
, const char *src
, size_t srclen
)
1521 char *dst
= box_text_offset(box
, cells
);
1524 strncpy(dst
, src
, srclen
);
1528 add_line_at(struct view
*view
, unsigned long pos
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1531 unsigned long lineno
;
1533 if (!realloc_lines(&view
->line
, view
->lines
, 1))
1537 void *alloc_data
= calloc(1, data_size
);
1543 memcpy(alloc_data
, data
, data_size
);
1547 if (pos
< view
->lines
) {
1549 line
= view
->line
+ pos
;
1550 lineno
= line
->lineno
;
1552 memmove(line
+ 1, line
, (view
->lines
- pos
) * sizeof(*view
->line
));
1553 while (pos
< view
->lines
) {
1554 view
->line
[pos
].lineno
++;
1555 view
->line
[pos
++].dirty
= 1;
1558 line
= &view
->line
[view
->lines
++];
1559 lineno
= view
->lines
- view
->custom_lines
;
1562 memset(line
, 0, sizeof(*line
));
1564 line
->data
= (void *) data
;
1568 view
->custom_lines
++;
1570 line
->lineno
= lineno
;
1576 add_line(struct view
*view
, const void *data
, enum line_type type
, size_t data_size
, bool custom
)
1578 return add_line_at(view
, view
->lines
, data
, type
, data_size
, custom
);
1582 add_line_alloc_(struct view
*view
, void **ptr
, enum line_type type
, size_t data_size
, bool custom
)
1584 struct line
*line
= add_line(view
, NULL
, type
, data_size
, custom
);
1592 add_line_nodata(struct view
*view
, enum line_type type
)
1594 return add_line(view
, NULL
, type
, 0, false);
1598 add_line_text_at_(struct view
*view
, unsigned long pos
, const char *text
, size_t textlen
, enum line_type type
, size_t cells
, bool custom
)
1601 struct line
*line
= add_line_at(view
, pos
, NULL
, type
, box_sizeof(NULL
, cells
, textlen
), custom
);
1607 box
->cell
[box
->cells
].length
= textlen
;
1608 box
->cell
[box
->cells
++].type
= type
;
1609 box_text_copy(box
, cells
, text
, textlen
);
1611 if (view
->ops
->column_bits
)
1612 view_column_info_update(view
, line
);
1617 add_line_text_at(struct view
*view
, unsigned long pos
, const char *text
, enum line_type type
, size_t cells
)
1619 return add_line_text_at_(view
, pos
, text
, strlen(text
), type
, cells
, false);
1623 add_line_text(struct view
*view
, const char *text
, enum line_type type
)
1625 return add_line_text_at(view
, view
->lines
, text
, type
, 1);
1628 struct line
* PRINTF_LIKE(3, 4)
1629 add_line_format(struct view
*view
, enum line_type type
, const char *fmt
, ...)
1631 char buf
[SIZEOF_STR
];
1634 FORMAT_BUFFER(buf
, sizeof(buf
), fmt
, retval
, false);
1635 return retval
>= 0 ? add_line_text(view
, buf
, type
) : NULL
;
1639 append_line_format(struct view
*view
, struct line
*line
, const char *fmt
, ...)
1641 struct box
*box
= line
->data
;
1642 size_t textlen
= box_text_length(box
);
1647 va_start(args
, fmt
);
1648 fmtlen
= vsnprintf(NULL
, 0, fmt
, args
);
1654 box
= realloc(box
, box_sizeof(box
, 0, fmtlen
));
1658 box
->text
= text
= box_text_offset(box
, box
->cells
);
1659 FORMAT_BUFFER(text
+ textlen
, fmtlen
+ 1, fmt
, retval
, false);
1663 box
->cell
[box
->cells
- 1].length
+= fmtlen
;
1667 if (view
->ops
->column_bits
)
1668 view_column_info_update(view
, line
);
1674 * Global view state.
1677 /* Included last to not pollute the rest of the file. */
1678 #include "tig/main.h"
1679 #include "tig/diff.h"
1680 #include "tig/log.h"
1681 #include "tig/tree.h"
1682 #include "tig/blob.h"
1683 #include "tig/blame.h"
1684 #include "tig/refs.h"
1685 #include "tig/status.h"
1686 #include "tig/stage.h"
1687 #include "tig/stash.h"
1688 #include "tig/grep.h"
1689 #include "tig/pager.h"
1690 #include "tig/help.h"
1692 static struct view
*views
[] = {
1693 #define VIEW_DATA(id, name) &name##_view
1694 VIEW_INFO(VIEW_DATA
)
1700 return 0 <= i
&& i
< ARRAY_SIZE(views
) ? views
[i
] : NULL
;
1703 /* vim: set ts=8 sw=8 noexpandtab: */