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.
15 #include "tig/parse.h"
17 #include "tig/prompt.h"
18 #include "tig/display.h"
22 struct file_finder_line
{
27 DEFINE_ALLOCATOR(realloc_file_array
, struct file_finder_line
*, 256)
33 struct file_finder_line
**file
;
35 struct file_finder_line
**line
;
44 file_finder_read(struct file_finder
*finder
, const char *commit
)
46 const char *tree
= string_rev_is_null(commit
) ? "HEAD" : commit
;
47 const char *ls_tree_files_argv
[] = {
48 "git", "ls-tree", "-z", "-r", "--name-only", "--full-name",
56 if (!io_run(&io
, IO_RD
, repo
.cdup
, NULL
, ls_tree_files_argv
))
59 for (files
= 0; io_get(&io
, &buf
, 0, TRUE
); files
++) {
60 /* Alloc two to ensure NULL terminated array. */
61 if (!realloc_file_array(&finder
->file
, files
, 2)) {
66 finder
->file
[files
] = calloc(1, sizeof(*finder
->file
) + buf
.size
);
67 if (!finder
->file
[files
]) {
72 strncpy(finder
->file
[files
]->text
, buf
.data
, buf
.size
);
75 if (io_error(&io
) || !realloc_file_array(&finder
->line
, 0, files
+ 1))
82 file_finder_done(struct file_finder
*finder
)
88 for (i
= 0; finder
->file
[i
]; i
++)
89 free(finder
->file
[i
]);
98 file_finder_move(struct file_finder
*finder
, int direction
)
100 if (direction
< 0 && finder
->pos
.lineno
<= -direction
)
101 finder
->pos
.lineno
= 0;
103 finder
->pos
.lineno
+= direction
;
105 if (finder
->pos
.lineno
>= finder
->lines
)
106 finder
->pos
.lineno
= finder
->lines
- 1;
108 if (finder
->pos
.offset
+ finder
->height
<= finder
->pos
.lineno
)
109 finder
->pos
.offset
= finder
->pos
.lineno
- (finder
->height
/ 2);
111 if (finder
->pos
.offset
> finder
->pos
.lineno
)
112 finder
->pos
.offset
= finder
->pos
.lineno
;
114 if (finder
->lines
<= finder
->height
)
115 finder
->pos
.offset
= 0;
119 file_finder_draw_line(struct file_finder
*finder
, struct file_finder_line
*line
)
121 const char **search
= finder
->search
;
122 const char *text
= line
->text
;
125 for (; *text
&& search
&& *search
&& (pos
= strstr(text
, *search
)); search
++) {
127 waddnstr(finder
->win
, text
, pos
- text
);
128 wattron(finder
->win
, A_STANDOUT
);
129 waddnstr(finder
->win
, pos
, 1);
130 wattroff(finder
->win
, A_STANDOUT
);
135 waddstr(finder
->win
, text
);
139 file_finder_draw(struct file_finder
*finder
)
141 struct position
*pos
= &finder
->pos
;
142 struct file_finder_line
*current_line
= finder
->line
[pos
->lineno
];
143 struct file_finder_line
**line_pos
= &finder
->line
[pos
->offset
];
146 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_DEFAULT
));
149 for (column
= 0; *line_pos
&& column
< finder
->height
- 1; line_pos
++) {
150 struct file_finder_line
*line
= *line_pos
;
152 if (finder
->searchlen
!= line
->matches
)
155 wmove(finder
->win
, column
++, 0);
156 if (line
== current_line
) {
157 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_CURSOR
));
159 file_finder_draw_line(finder
, line
);
160 if (line
== current_line
) {
161 wclrtoeol(finder
->win
);
162 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_DEFAULT
));
166 wmove(finder
->win
, finder
->height
- 1, 0);
167 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_TITLE_FOCUS
));
168 wprintw(finder
->win
, "[finder] file %d of %d", pos
->lineno
+ 1, finder
->lines
);
169 wclrtoeol(finder
->win
);
170 wrefresh(finder
->win
);
174 file_finder_line_matches(struct file_finder_line
*line
, const char **search
)
176 const char *text
= line
->text
;
180 for (; *text
&& *search
&& (pos
= strstr(text
, *search
)); search
++) {
181 text
= pos
+ strlen(*search
);
189 file_finder_update(struct file_finder
*finder
)
191 struct file_finder_line
*current
= finder
->line
[finder
->pos
.lineno
];
192 size_t new_lineno
= 0;
195 memset(finder
->line
, 0, sizeof(finder
->line
) * finder
->lines
);
198 for (i
= 0; finder
->file
[i
]; i
++) {
199 struct file_finder_line
*line
= finder
->file
[i
];
204 if (line
->matches
+ 1 < finder
->searchlen
) {
208 if (line
->matches
>= finder
->searchlen
) {
209 line
->matches
= finder
->searchlen
;
211 line
->matches
= file_finder_line_matches(line
, finder
->search
);
212 if (line
->matches
< finder
->searchlen
)
219 finder
->line
[finder
->lines
++] = line
;
222 finder
->pos
.lineno
= new_lineno
;
225 static enum input_status
226 file_finder_input_handler(struct input
*input
, struct key
*key
)
228 struct file_finder
*finder
= input
->data
;
229 enum input_status status
;
231 status
= prompt_default_handler(input
, key
);
232 if (status
== INPUT_DELETE
) {
233 if (finder
->searchlen
> 0) {
235 free((void *) finder
->search
[finder
->searchlen
]);
236 finder
->search
[finder
->searchlen
] = NULL
;
238 file_finder_update(finder
);
239 file_finder_move(finder
, 0);
240 file_finder_draw(finder
);
244 if (status
!= INPUT_SKIP
)
247 switch (key_to_value(key
)) {
249 argv_append(&finder
->search
, key
->data
.bytes
);
251 file_finder_update(finder
);
252 file_finder_move(finder
, 0);
253 file_finder_draw(finder
);
257 file_finder_move(finder
, -1);
258 file_finder_draw(finder
);
262 file_finder_move(finder
, +1);
263 file_finder_draw(finder
);
267 /* Catch all non-multibyte keys. */
273 open_file_finder(const char *commit
)
275 struct file_finder finder
= {0};
276 const char *file
= NULL
;
278 if (!file_finder_read(&finder
, commit
)) {
279 file_finder_done(&finder
);
283 getmaxyx(stdscr
, finder
.height
, finder
.width
);
285 finder
.win
= newwin(finder
.height
, finder
.width
, 0, 0);
287 file_finder_done(&finder
);
291 file_finder_update(&finder
);
292 file_finder_draw(&finder
);
293 if (read_prompt_incremental("Find file: ", FALSE
, file_finder_input_handler
, &finder
))
294 file
= get_path(finder
.line
[finder
.pos
.lineno
]->text
);
296 file_finder_done(&finder
);
297 redraw_display(TRUE
);
301 /* vim: set ts=8 sw=8 noexpandtab: */