Merge pull request #530 from jjlin/master
[tig.git] / src / grep.c
blobafa825385164fc10e062b8471573d94cba7b5cee
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.
14 #include "tig/refdb.h"
15 #include "tig/options.h"
16 #include "tig/parse.h"
17 #include "tig/repo.h"
18 #include "tig/display.h"
19 #include "tig/prompt.h"
20 #include "tig/draw.h"
21 #include "tig/blob.h"
22 #include "tig/grep.h"
24 struct grep_line {
25 const char *file;
26 unsigned long lineno;
27 char text[1];
30 struct grep_state {
31 const char *last_file;
32 bool no_file_group;
35 static struct grep_line *
36 grep_get_line(const struct line *line)
38 static struct grep_line grep_line;
40 if (line->type == LINE_DEFAULT)
41 return line->data;
43 grep_line.file = line->type == LINE_DELIMITER ? "" : get_path(box_text(line));
44 return &grep_line;
47 static bool
48 grep_get_column_data(struct view *view, const struct line *line, struct view_column_data *column_data)
50 struct grep_line *grep = grep_get_line(line);
52 if (line->type == LINE_DELIMITER) {
53 static struct view_column separator_column;
55 separator_column.type = VIEW_COLUMN_TEXT;
56 column_data->section = &separator_column;
57 column_data->text = "--";
58 return true;
61 if (*grep->file && !*grep->text) {
62 static struct view_column file_name_column;
64 file_name_column.type = VIEW_COLUMN_FILE_NAME;
65 file_name_column.opt.file_name.display = FILENAME_ALWAYS;
67 column_data->section = &file_name_column;
70 column_data->line_number = &grep->lineno;
71 column_data->file_name = grep->file;
72 column_data->text = grep->text;
73 return true;
76 static void
77 grep_select(struct view *view, struct line *line)
79 struct grep_line *grep = grep_get_line(line);
81 if (!*grep->file)
82 return;
83 view->env->ref[0] = 0;
84 string_ncopy(view->env->file, grep->file, strlen(grep->file));
85 string_ncopy(view->ref, grep->file, strlen(grep->file));
86 view->env->lineno = grep->lineno + 1;
89 static const char *grep_args[] = {
90 "git", "grep", "--no-color", "-n", "-z", "--full-name", NULL
93 static const char **grep_argv;
95 static bool
96 grep_prompt(void)
98 const char *argv[SIZEOF_ARG];
99 int argc = 0;
100 char *grep = read_prompt("grep: ");
102 if (!grep || !argv_from_string_no_quotes(argv, &argc, grep))
103 return false;
104 if (grep_argv)
105 argv_free(grep_argv);
106 return argv_append_array(&grep_argv, argv);
109 void
110 open_grep_view(struct view *prev)
112 struct view *view = &grep_view;
113 bool in_grep_view = prev == view;
115 if ((!prev && is_initial_view(view)) || (view->lines && !in_grep_view)) {
116 open_view(prev, view, OPEN_DEFAULT);
117 } else {
118 if (grep_prompt()) {
119 clear_position(&view->pos);
120 open_view(prev, view, OPEN_RELOAD);
125 static bool
126 grep_open(struct view *view, enum open_flags flags)
128 struct grep_state *state = view->private;
129 const char **argv = NULL;
131 if (is_initial_view(view)) {
132 grep_argv = opt_cmdline_args;
133 opt_cmdline_args = NULL;
136 if (!argv_append_array(&argv, grep_args) ||
137 !argv_append_array(&argv, grep_argv))
138 return false;
141 struct view_column *column = get_view_column(view, VIEW_COLUMN_FILE_NAME);
143 state->no_file_group = !column || column->opt.file_name.display != FILENAME_NO;
146 return begin_update(view, NULL, argv, flags);
149 static enum request
150 grep_request(struct view *view, enum request request, struct line *line)
152 struct grep_state *state = view->private;
153 struct grep_line *grep = grep_get_line(line);
154 struct view *file_view = &blob_view;
156 switch (request) {
157 case REQ_REFRESH:
158 refresh_view(view);
159 return REQ_NONE;
161 case REQ_ENTER:
162 if (!*grep->file)
163 return REQ_NONE;
164 if (file_view->parent == view && file_view->prev == view &&
165 state->last_file == grep->file && view_is_displayed(file_view)) {
166 if (*grep->text) {
167 select_view_line(file_view, grep->lineno);
168 update_view_title(file_view);
171 } else {
172 const char *file_argv[] = { repo.cdup, grep->file, NULL };
174 clear_position(&file_view->pos);
175 view->env->goto_lineno = grep->lineno;
176 view->env->blob[0] = 0;
177 open_argv(view, file_view, file_argv, repo.cdup, OPEN_SPLIT | OPEN_RELOAD);
179 state->last_file = grep->file;
180 return REQ_NONE;
182 case REQ_EDIT:
183 if (!*grep->file)
184 return request;
185 open_editor(grep->file, grep->lineno + 1);
186 return REQ_NONE;
188 case REQ_VIEW_BLAME:
189 view->env->ref[0] = 0;
190 view->env->goto_lineno = grep->lineno;
191 return request;
193 default:
194 return request;
198 static bool
199 grep_read(struct view *view, struct buffer *buf, bool force_stop)
201 struct grep_state *state = view->private;
202 struct grep_line *grep;
203 char *lineno, *text;
204 struct line *line;
205 const char *file;
206 size_t textlen;
208 if (!buf) {
209 state->last_file = NULL;
210 if (!view->lines) {
211 view->ref[0] = 0;
212 report("No matches found");
214 return true;
217 if (!strcmp(buf->data, "--"))
218 return add_line_nodata(view, LINE_DELIMITER) != NULL;
220 lineno = io_memchr(buf, buf->data, 0);
221 text = io_memchr(buf, lineno, 0);
224 * No data indicates binary file matches, e.g.:
225 * > git grep vertical- -- test
226 * test/graph/20-tig-all-long-test:● │ Add "auto" vertical-split
227 * Binary file test/graph/20-tig-all-long-test.in matches
229 if (!lineno || !text)
230 return true;
232 textlen = strlen(text);
234 file = get_path(buf->data);
235 if (!file)
236 return false;
238 if (!state->no_file_group && file != state->last_file &&
239 !add_line_text(view, file, LINE_FILE))
240 return false;
242 line = add_line_alloc(view, &grep, LINE_DEFAULT, textlen, false);
243 if (!line)
244 return false;
246 grep->file = file;
247 grep->lineno = atoi(lineno);
248 if (grep->lineno > 0)
249 grep->lineno -= 1;
250 strncpy(grep->text, text, textlen);
251 grep->text[textlen] = 0;
252 view_column_info_update(view, line);
254 state->last_file = file;
256 return true;
259 static struct view_ops grep_ops = {
260 "line",
262 VIEW_REFRESH | VIEW_GREP_LIKE,
263 sizeof(struct grep_state),
264 grep_open,
265 grep_read,
266 view_column_draw,
267 grep_request,
268 view_column_grep,
269 grep_select,
270 NULL,
271 view_column_bit(FILE_NAME) | view_column_bit(LINE_NUMBER) |
272 view_column_bit(TEXT),
273 grep_get_column_data,
276 DEFINE_VIEW(grep);
278 /* vim: set ts=8 sw=8 noexpandtab: */