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
;
39 struct keymap
*keymap
;
45 file_finder_read(struct file_finder
*finder
, const char *commit
)
47 const char *tree
= string_rev_is_null(commit
) ? "HEAD" : commit
;
48 const char *ls_tree_files_argv
[] = {
49 "git", "ls-tree", "-z", "-r", "--name-only", "--full-name",
57 if (!io_run(&io
, IO_RD
, repo
.cdup
, NULL
, ls_tree_files_argv
))
60 for (files
= 0; io_get(&io
, &buf
, 0, true); files
++) {
61 /* Alloc two to ensure NULL terminated array. */
62 if (!realloc_file_array(&finder
->file
, files
, 2)) {
67 finder
->file
[files
] = calloc(1, sizeof(*finder
->file
) + buf
.size
);
68 if (!finder
->file
[files
]) {
73 strncpy(finder
->file
[files
]->text
, buf
.data
, buf
.size
);
76 if (io_error(&io
) || !realloc_file_array(&finder
->line
, 0, files
+ 1))
83 file_finder_done(struct file_finder
*finder
)
89 for (i
= 0; finder
->file
[i
]; i
++)
90 free(finder
->file
[i
]);
99 file_finder_move(struct file_finder
*finder
, int direction
)
101 if (direction
< 0 && finder
->pos
.lineno
<= -direction
)
102 finder
->pos
.lineno
= 0;
104 finder
->pos
.lineno
+= direction
;
106 if (finder
->pos
.lineno
>= finder
->lines
)
107 finder
->pos
.lineno
= finder
->lines
- 1;
109 if (finder
->pos
.offset
+ finder
->height
<= finder
->pos
.lineno
)
110 finder
->pos
.offset
= finder
->pos
.lineno
- (finder
->height
/ 2);
112 if (finder
->pos
.offset
> finder
->pos
.lineno
)
113 finder
->pos
.offset
= finder
->pos
.lineno
;
115 if (finder
->lines
<= finder
->height
)
116 finder
->pos
.offset
= 0;
120 file_finder_draw_line(struct file_finder
*finder
, struct file_finder_line
*line
)
122 const char **search
= finder
->search
;
123 const char *text
= line
->text
;
126 for (; *text
&& search
&& *search
&& (pos
= strstr(text
, *search
)); search
++) {
128 waddnstr(finder
->win
, text
, pos
- text
);
129 wattron(finder
->win
, A_STANDOUT
);
130 waddnstr(finder
->win
, pos
, 1);
131 wattroff(finder
->win
, A_STANDOUT
);
136 waddstr(finder
->win
, text
);
140 file_finder_draw(struct file_finder
*finder
)
142 struct position
*pos
= &finder
->pos
;
143 struct file_finder_line
*current_line
= finder
->line
[pos
->lineno
];
144 struct file_finder_line
**line_pos
= &finder
->line
[pos
->offset
];
147 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_DEFAULT
));
150 for (column
= 0; *line_pos
&& column
< finder
->height
- 1; line_pos
++) {
151 struct file_finder_line
*line
= *line_pos
;
153 if (finder
->searchlen
!= line
->matches
)
156 wmove(finder
->win
, column
++, 0);
157 if (line
== current_line
) {
158 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_CURSOR
));
160 file_finder_draw_line(finder
, line
);
161 if (line
== current_line
) {
162 wclrtoeol(finder
->win
);
163 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_DEFAULT
));
167 wmove(finder
->win
, finder
->height
- 1, 0);
168 wbkgdset(finder
->win
, get_line_attr(NULL
, LINE_TITLE_FOCUS
));
169 wprintw(finder
->win
, "[finder] file %d of %d", pos
->lineno
+ 1, finder
->lines
);
170 wclrtoeol(finder
->win
);
171 wrefresh(finder
->win
);
175 file_finder_line_matches(struct file_finder_line
*line
, const char **search
)
177 const char *text
= line
->text
;
181 for (; *text
&& *search
&& (pos
= strstr(text
, *search
)); search
++) {
182 text
= pos
+ strlen(*search
);
190 file_finder_update(struct file_finder
*finder
)
192 struct file_finder_line
*current
= finder
->line
[finder
->pos
.lineno
];
193 size_t new_lineno
= 0;
196 memset(finder
->line
, 0, sizeof(finder
->line
) * finder
->lines
);
199 for (i
= 0; finder
->file
&& finder
->file
[i
]; i
++) {
200 struct file_finder_line
*line
= finder
->file
[i
];
205 if (line
->matches
+ 1 < finder
->searchlen
) {
209 if (line
->matches
>= finder
->searchlen
) {
210 line
->matches
= finder
->searchlen
;
212 line
->matches
= file_finder_line_matches(line
, finder
->search
);
213 if (line
->matches
< finder
->searchlen
)
220 finder
->line
[finder
->lines
++] = line
;
223 finder
->pos
.lineno
= new_lineno
;
226 static enum input_status
227 file_finder_input_handler(struct input
*input
, struct key
*key
)
229 struct file_finder
*finder
= input
->data
;
230 enum input_status status
;
232 status
= prompt_default_handler(input
, key
);
233 if (status
== INPUT_DELETE
) {
234 if (finder
->searchlen
> 0) {
236 free((void *) finder
->search
[finder
->searchlen
]);
237 finder
->search
[finder
->searchlen
] = NULL
;
239 file_finder_update(finder
);
240 file_finder_move(finder
, 0);
241 file_finder_draw(finder
);
245 if (status
!= INPUT_SKIP
)
248 switch (get_keybinding(finder
->keymap
, key
, 1, NULL
)) {
250 file_finder_move(finder
, -1);
251 file_finder_draw(finder
);
255 file_finder_move(finder
, +1);
256 file_finder_draw(finder
);
260 if (key_to_value(key
) == 0) {
261 argv_append(&finder
->search
, key
->data
.bytes
);
263 file_finder_update(finder
);
264 file_finder_move(finder
, 0);
265 file_finder_draw(finder
);
269 /* Catch all non-multibyte keys. */
275 open_file_finder(const char *commit
)
277 struct file_finder finder
= {0};
278 const char *file
= NULL
;
280 if (!file_finder_read(&finder
, commit
)) {
281 file_finder_done(&finder
);
285 getmaxyx(stdscr
, finder
.height
, finder
.width
);
287 finder
.win
= newwin(finder
.height
, finder
.width
, 0, 0);
289 file_finder_done(&finder
);
293 finder
.keymap
= get_keymap("search", STRING_SIZE("search")),
294 file_finder_update(&finder
);
295 file_finder_draw(&finder
);
296 if (read_prompt_incremental("Find file: ", false, true, file_finder_input_handler
, &finder
) && finder
.pos
.lineno
< finder
.lines
)
297 file
= get_path(finder
.line
[finder
.pos
.lineno
]->text
);
299 file_finder_done(&finder
);
300 redraw_display(true);
304 /* vim: set ts=8 sw=8 noexpandtab: */