blame: move functionality to colorize ID to draw_id()
[tig.git] / src / pager.c
blobda7cdb90671b468aae1a5a6bc031838c060616d4
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/options.h"
16 #include "tig/request.h"
17 #include "tig/line.h"
18 #include "tig/keys.h"
19 #include "tig/display.h"
20 #include "tig/view.h"
21 #include "tig/draw.h"
22 #include "tig/diff.h"
25 * Pager backend
28 bool
29 pager_draw(struct view *view, struct line *line, unsigned int lineno)
31 if (draw_lineno(view, lineno))
32 return TRUE;
34 if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
35 return TRUE;
37 draw_text(view, line->type, line->data);
38 return TRUE;
41 static bool
42 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
44 const char *describe_argv[] = { "git", "describe", commit_id, NULL };
45 char ref[SIZEOF_STR];
47 if (!io_run_buf(describe_argv, ref, sizeof(ref)) || !*ref)
48 return TRUE;
50 /* This is the only fatal call, since it can "corrupt" the buffer. */
51 if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
52 return FALSE;
54 return TRUE;
57 static void
58 add_pager_refs(struct view *view, const char *commit_id)
60 char buf[SIZEOF_STR];
61 struct ref_list *list;
62 size_t bufpos = 0, i;
63 const char *sep = "Refs: ";
64 bool is_tag = FALSE;
66 list = get_ref_list(commit_id);
67 if (!list) {
68 if (view_has_flags(view, VIEW_ADD_DESCRIBE_REF))
69 goto try_add_describe_ref;
70 return;
73 for (i = 0; i < list->size; i++) {
74 struct ref *ref = list->refs[i];
75 const struct ref_format *fmt = get_ref_format(ref);
77 if (!string_format_from(buf, &bufpos, "%s%s%s%s", sep,
78 fmt->start, ref->name, fmt->end))
79 return;
80 sep = ", ";
81 if (ref_is_tag(ref))
82 is_tag = TRUE;
85 if (!is_tag && view_has_flags(view, VIEW_ADD_DESCRIBE_REF)) {
86 try_add_describe_ref:
87 /* Add <tag>-g<commit_id> "fake" reference. */
88 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
89 return;
92 if (bufpos == 0)
93 return;
95 add_line_text(view, buf, LINE_PP_REFS);
98 static struct line *
99 pager_wrap_line(struct view *view, const char *data, enum line_type type)
101 size_t first_line = 0;
102 bool has_first_line = FALSE;
103 size_t datalen = strlen(data);
104 size_t lineno = 0;
106 while (datalen > 0 || !has_first_line) {
107 bool wrapped = !!first_line;
108 size_t linelen = string_expanded_length(data, datalen, opt_tab_size, view->width - !!wrapped);
109 struct line *line;
110 char *text;
112 line = add_line(view, NULL, type, linelen + 1, wrapped);
113 if (!line)
114 break;
115 if (!has_first_line) {
116 first_line = view->lines - 1;
117 has_first_line = TRUE;
120 if (!wrapped)
121 lineno = line->lineno;
123 line->wrapped = wrapped;
124 line->lineno = lineno;
125 text = line->data;
126 if (linelen)
127 strncpy(text, data, linelen);
128 text[linelen] = 0;
130 datalen -= linelen;
131 data += linelen;
134 return has_first_line ? &view->line[first_line] : NULL;
137 bool
138 pager_common_read(struct view *view, const char *data, enum line_type type)
140 struct line *line;
142 if (!data)
143 return TRUE;
145 if (opt_wrap_lines) {
146 line = pager_wrap_line(view, data, type);
147 } else {
148 line = add_line_text(view, data, type);
151 if (!line)
152 return FALSE;
154 if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_ADD_PAGER_REFS))
155 add_pager_refs(view, data + STRING_SIZE("commit "));
157 return TRUE;
160 bool
161 pager_read(struct view *view, char *data)
163 if (!data)
164 return TRUE;
166 return pager_common_read(view, data, get_line_type(data));
169 enum request
170 pager_request(struct view *view, enum request request, struct line *line)
172 int split = 0;
174 if (request != REQ_ENTER)
175 return request;
177 if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_OPEN_DIFF)) {
178 open_diff_view(view, OPEN_SPLIT);
179 split = 1;
182 /* Always scroll the view even if it was split. That way
183 * you can use Enter to scroll through the log view and
184 * split open each commit diff. */
185 scroll_view(view, REQ_SCROLL_LINE_DOWN);
187 /* FIXME: A minor workaround. Scrolling the view will call report_clear()
188 * but if we are scrolling a non-current view this won't properly
189 * update the view title. */
190 if (split)
191 update_view_title(view);
193 return REQ_NONE;
196 bool
197 pager_grep(struct view *view, struct line *line)
199 const char *text[] = { line->data, NULL };
201 return grep_text(view, text);
204 void
205 pager_select(struct view *view, struct line *line)
207 if (line->type == LINE_COMMIT) {
208 string_copy_rev_from_commit_line(view->env->commit, line->data);
209 if (!view_has_flags(view, VIEW_NO_REF))
210 string_copy_rev(view->ref, view->env->commit);
214 bool
215 pager_open(struct view *view, enum open_flags flags)
217 if (!open_from_stdin(flags) && !view->lines && !(flags & OPEN_PREPARED)) {
218 report("No pager content, press %s to run command from prompt",
219 get_view_key(view, REQ_PROMPT));
220 return FALSE;
223 return begin_update(view, NULL, NULL, flags);
226 static struct view_ops pager_ops = {
227 "line",
229 VIEW_OPEN_DIFF | VIEW_NO_REF | VIEW_NO_GIT_DIR,
231 pager_open,
232 pager_read,
233 pager_draw,
234 pager_request,
235 pager_grep,
236 pager_select,
239 DEFINE_VIEW(pager);
241 /* vim: set ts=8 sw=8 noexpandtab: */