Make search_view prompt for search string
[tig.git] / src / search.c
blob9861de855e9628deead3cb08bb64926c339c5ff7
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/search.h"
15 #include "tig/prompt.h"
16 #include "tig/display.h"
17 #include "tig/draw.h"
19 DEFINE_ALLOCATOR(realloc_unsigned_ints, unsigned int, 32)
21 bool
22 grep_text(struct view *view, const char *text[])
24 regmatch_t pmatch;
25 size_t i;
27 for (i = 0; text[i]; i++)
28 if (*text[i] && !regexec(view->regex, text[i], 1, &pmatch, 0))
29 return true;
30 return false;
33 static bool
34 find_matches(struct view *view)
36 size_t lineno;
38 /* Note, lineno is unsigned long so will wrap around in which case it
39 * will become bigger than view->lines. */
40 for (lineno = 0; lineno < view->lines; lineno++) {
41 if (!view->ops->grep(view, &view->line[lineno]))
42 continue;
44 if (!realloc_unsigned_ints(&view->matched_line, view->matched_lines, 1))
45 return false;
47 view->matched_line[view->matched_lines++] = lineno;
50 return true;
53 static void
54 setup_and_find_next(struct view *view, enum request request)
56 int regex_err;
57 int regex_flags = opt_ignore_case ? REG_ICASE : 0;
59 if (view->regex) {
60 regfree(view->regex);
61 *view->grep = 0;
62 } else {
63 view->regex = calloc(1, sizeof(*view->regex));
64 if (!view->regex)
65 return;
68 regex_err = regcomp(view->regex, view->env->search, REG_EXTENDED | regex_flags);
69 if (regex_err != 0) {
70 char buf[SIZEOF_STR] = "unknown error";
72 regerror(regex_err, view->regex, buf, sizeof(buf));
73 report("Search failed: %s", buf);
74 return;
77 string_copy(view->grep, view->env->search);
79 reset_search(view);
81 find_next(view, request);
84 void
85 find_next(struct view *view, enum request request)
87 int direction;
88 size_t i;
90 if (!*view->grep) {
91 if (!*view->env->search)
92 report("No previous search");
93 else
94 setup_and_find_next(view, request);
95 return;
98 switch (request) {
99 case REQ_SEARCH:
100 case REQ_FIND_NEXT:
101 direction = 1;
102 break;
104 case REQ_SEARCH_BACK:
105 case REQ_FIND_PREV:
106 direction = -1;
107 break;
109 default:
110 return;
113 if (!view->matched_lines && !find_matches(view)) {
114 report("Allocation failure");
115 return;
118 /* Note, `i` is unsigned and will wrap around in which case it
119 * will become bigger than view->matched_lines. */
120 i = direction > 0 ? 0 : view->matched_lines - 1;
121 for (; i < view->matched_lines; i += direction) {
122 size_t lineno = view->matched_line[i];
124 if (direction > 0 && lineno <= view->pos.lineno)
125 continue;
127 if (direction < 0 && lineno >= view->pos.lineno)
128 continue;
130 select_view_line(view, lineno);
131 report("Line %zu matches '%s' (%zu of %zu)", lineno + 1, view->grep, i + 1, view->matched_lines);
132 return;
135 report("No match found for '%s'", view->grep);
138 void
139 reset_search(struct view *view)
141 free(view->matched_line);
142 view->matched_line = NULL;
143 view->matched_lines = 0;
146 void
147 search_view(struct view *view, enum request request)
149 const char *prompt = request == REQ_SEARCH ? "/" : "?";
150 char *search = read_prompt(prompt);
152 if (search) {
153 string_ncopy(argv_env.search, search, strlen(search));
154 setup_and_find_next(view, request);
155 } else if (*argv_env.search) {
156 find_next(view, request);
157 } else {
158 report_clear();
162 /* vim: set ts=8 sw=8 noexpandtab: */