| 23 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,89 @@ |
| 1 |
+#define _GNU_SOURCE |
|
| 2 |
+#include <stdlib.h> |
|
| 3 |
+ |
|
| 4 |
+#include "choices.h" |
|
| 5 |
+#include "fzy.h" |
|
| 6 |
+ |
|
| 7 |
+#define INITIAL_CAPACITY 1 |
|
| 8 |
+ |
|
| 9 |
+static int cmpchoice(size_t *idx1, size_t *idx2, double *choices_score) {
|
|
| 10 |
+ double score1 = choices_score[*idx1]; |
|
| 11 |
+ double score2 = choices_score[*idx2]; |
|
| 12 |
+ |
|
| 13 |
+ if(score1 == score2) |
|
| 14 |
+ return 0; |
|
| 15 |
+ else if(score1 < score2) |
|
| 16 |
+ return 1; |
|
| 17 |
+ else |
|
| 18 |
+ return -1; |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+static void choices_resize(choices_t *c, int new_capacity){
|
|
| 22 |
+ c->strings = realloc(c->strings, new_capacity * sizeof(const char *)); |
|
| 23 |
+ c->scores = realloc(c->scores, new_capacity * sizeof(double)); |
|
| 24 |
+ c->sorted = realloc(c->sorted, new_capacity * sizeof(size_t)); |
|
| 25 |
+ |
|
| 26 |
+ for(int i = c->capacity; i < new_capacity; i++){
|
|
| 27 |
+ c->strings[i] = NULL; |
|
| 28 |
+ } |
|
| 29 |
+ c->capacity = new_capacity; |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+void choices_init(choices_t *c){
|
|
| 33 |
+ c->strings = NULL; |
|
| 34 |
+ c->scores = NULL; |
|
| 35 |
+ c->sorted = NULL; |
|
| 36 |
+ c->capacity = c->size = 0; |
|
| 37 |
+ c->selection = c->available = 0; |
|
| 38 |
+ choices_resize(c, INITIAL_CAPACITY); |
|
| 39 |
+} |
|
| 40 |
+void choices_free(choices_t *c){
|
|
| 41 |
+ free(c->strings); |
|
| 42 |
+ free(c->scores); |
|
| 43 |
+ free(c->sorted); |
|
| 44 |
+}; |
|
| 45 |
+ |
|
| 46 |
+void choices_add(choices_t *c, const char *choice){
|
|
| 47 |
+ if(c->size == c->capacity){
|
|
| 48 |
+ choices_resize(c, c->capacity * 2); |
|
| 49 |
+ } |
|
| 50 |
+ c->strings[c->size++] = choice; |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+size_t choices_available(choices_t *c){
|
|
| 54 |
+ return c->available; |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+void choices_search(choices_t *c, const char *search){
|
|
| 58 |
+ c->selection = 0; |
|
| 59 |
+ c->available = 0; |
|
| 60 |
+ |
|
| 61 |
+ for(size_t i = 0; i < c->size; i++){
|
|
| 62 |
+ if(has_match(search, c->strings[i])){
|
|
| 63 |
+ c->scores[i] = match(search, c->strings[i]); |
|
| 64 |
+ c->sorted[c->available++] = i; |
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ qsort_r(c->sorted, c->available, sizeof(size_t), (int (*)(const void *, const void *, void *))cmpchoice, c->scores); |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+const char *choices_get(choices_t *c, size_t n){
|
|
| 72 |
+ if(n < c->available){
|
|
| 73 |
+ return c->strings[c->sorted[n]]; |
|
| 74 |
+ }else{
|
|
| 75 |
+ return NULL; |
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+double choices_getscore(choices_t *c, size_t n){
|
|
| 79 |
+ return c->scores[c->sorted[n]];; |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+void choices_prev(choices_t *c){
|
|
| 83 |
+ c->selection = (c->selection + c->available - 1) % c->available; |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+void choices_next(choices_t *c){
|
|
| 87 |
+ c->selection = (c->selection + 1) % c->available; |
|
| 88 |
+} |
|
| 89 |
+ |
| 0 | 90 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 1 |
+typedef struct {
|
|
| 2 |
+ size_t capacity; |
|
| 3 |
+ size_t size; |
|
| 4 |
+ |
|
| 5 |
+ const char **strings; |
|
| 6 |
+ double *scores; |
|
| 7 |
+ |
|
| 8 |
+ size_t *sorted; |
|
| 9 |
+ size_t available; |
|
| 10 |
+ size_t selection; |
|
| 11 |
+} choices_t; |
|
| 12 |
+ |
|
| 13 |
+void choices_init(choices_t *c); |
|
| 14 |
+void choices_free(choices_t *c); |
|
| 15 |
+void choices_add(choices_t *c, const char *choice); |
|
| 16 |
+size_t choices_available(choices_t *c); |
|
| 17 |
+void choices_search(choices_t *c, const char *search); |
|
| 18 |
+const char *choices_get(choices_t *c, size_t n); |
|
| 19 |
+double choices_getscore(choices_t *c, size_t n); |
|
| 20 |
+void choices_prev(choices_t *c); |
|
| 21 |
+void choices_next(choices_t *c); |
|
| 22 |
+ |
| ... | ... |
@@ -7,41 +7,14 @@ |
| 7 | 7 |
|
| 8 | 8 |
#include "fzy.h" |
| 9 | 9 |
#include "tty.h" |
| 10 |
+#include "choices.h" |
|
| 10 | 11 |
|
| 11 | 12 |
int flag_show_scores = 0; |
| 12 | 13 |
|
| 13 | 14 |
size_t num_lines = 10; |
| 14 | 15 |
size_t scrolloff = 1; |
| 15 | 16 |
|
| 16 |
- |
|
| 17 |
-#define INITIAL_CAPACITY 1 |
|
| 18 |
-int choices_capacity = 0; |
|
| 19 |
-int choices_n = 0; |
|
| 20 |
-const char **choices = NULL; |
|
| 21 |
-double *choices_score = NULL; |
|
| 22 |
-size_t *choices_sorted = NULL; |
|
| 23 |
-size_t current_selection = 0; |
|
| 24 |
- |
|
| 25 |
-void resize_choices(int new_capacity){
|
|
| 26 |
- choices = realloc(choices, new_capacity * sizeof(const char *)); |
|
| 27 |
- choices_score = realloc(choices_score, new_capacity * sizeof(double)); |
|
| 28 |
- choices_sorted = realloc(choices_sorted, new_capacity * sizeof(size_t)); |
|
| 29 |
- |
|
| 30 |
- int i = choices_capacity; |
|
| 31 |
- for(; i < new_capacity; i++){
|
|
| 32 |
- choices[i] = NULL; |
|
| 33 |
- } |
|
| 34 |
- choices_capacity = new_capacity; |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-void add_choice(const char *choice){
|
|
| 38 |
- if(choices_n == choices_capacity){
|
|
| 39 |
- resize_choices(choices_capacity * 2); |
|
| 40 |
- } |
|
| 41 |
- choices[choices_n++] = choice; |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-void read_choices(){
|
|
| 17 |
+void read_choices(choices_t *c){
|
|
| 45 | 18 |
char *line = NULL; |
| 46 | 19 |
size_t len = 0; |
| 47 | 20 |
ssize_t read; |
| ... | ... |
@@ -51,44 +24,13 @@ void read_choices(){
|
| 51 | 24 |
if((nl = strchr(line, '\n'))) |
| 52 | 25 |
*nl = '\0'; |
| 53 | 26 |
|
| 54 |
- add_choice(line); |
|
| 27 |
+ choices_add(c, line); |
|
| 55 | 28 |
|
| 56 | 29 |
line = NULL; |
| 57 | 30 |
} |
| 58 | 31 |
free(line); |
| 59 | 32 |
} |
| 60 | 33 |
|
| 61 |
-size_t choices_available = 0; |
|
| 62 |
- |
|
| 63 |
-static int cmpchoice(const void *p1, const void *p2) {
|
|
| 64 |
- size_t idx1 = *(size_t *)p1; |
|
| 65 |
- size_t idx2 = *(size_t *)p2; |
|
| 66 |
- |
|
| 67 |
- double score1 = choices_score[idx1]; |
|
| 68 |
- double score2 = choices_score[idx2]; |
|
| 69 |
- |
|
| 70 |
- if(score1 == score2) |
|
| 71 |
- return 0; |
|
| 72 |
- else if(score1 < score2) |
|
| 73 |
- return 1; |
|
| 74 |
- else |
|
| 75 |
- return -1; |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-void run_search(char *needle){
|
|
| 79 |
- current_selection = 0; |
|
| 80 |
- choices_available = 0; |
|
| 81 |
- int i; |
|
| 82 |
- for(i = 0; i < choices_n; i++){
|
|
| 83 |
- if(has_match(needle, choices[i])){
|
|
| 84 |
- choices_score[i] = match(needle, choices[i]); |
|
| 85 |
- choices_sorted[choices_available++] = i; |
|
| 86 |
- } |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- qsort(choices_sorted, choices_available, sizeof(size_t), cmpchoice); |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 | 34 |
#define SEARCH_SIZE_MAX 4096 |
| 93 | 35 |
int search_size; |
| 94 | 36 |
char search[SEARCH_SIZE_MAX + 1] = {0};
|
| ... | ... |
@@ -131,12 +73,13 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 131 | 73 |
tty_setnormal(tty); |
| 132 | 74 |
} |
| 133 | 75 |
|
| 134 |
-void draw(tty_t *tty){
|
|
| 76 |
+void draw(tty_t *tty, choices_t *choices){
|
|
| 135 | 77 |
size_t start = 0; |
| 78 |
+ size_t current_selection = choices->selection; |
|
| 136 | 79 |
if(current_selection + scrolloff >= num_lines){
|
| 137 | 80 |
start = current_selection + scrolloff - num_lines + 1; |
| 138 |
- if(start + num_lines >= choices_available){
|
|
| 139 |
- start = choices_available - num_lines; |
|
| 81 |
+ if(start + num_lines >= choices_available(choices)){
|
|
| 82 |
+ start = choices_available(choices) - num_lines; |
|
| 140 | 83 |
} |
| 141 | 84 |
} |
| 142 | 85 |
const char *prompt = "> "; |
| ... | ... |
@@ -144,9 +87,9 @@ void draw(tty_t *tty){
|
| 144 | 87 |
tty_printf(tty, "%s%s", prompt, search); |
| 145 | 88 |
for(size_t i = start; i < start + num_lines; i++){
|
| 146 | 89 |
tty_newline(tty); |
| 147 |
- if(i < choices_available){
|
|
| 148 |
- size_t choice_idx = choices_sorted[i]; |
|
| 149 |
- draw_match(tty, choices[choice_idx], i == current_selection); |
|
| 90 |
+ const char *choice = choices_get(choices, i); |
|
| 91 |
+ if(choice){
|
|
| 92 |
+ draw_match(tty, choice, i == choices->selection); |
|
| 150 | 93 |
} |
| 151 | 94 |
} |
| 152 | 95 |
tty_clearline(tty); |
| ... | ... |
@@ -155,13 +98,11 @@ void draw(tty_t *tty){
|
| 155 | 98 |
tty_flush(tty); |
| 156 | 99 |
} |
| 157 | 100 |
|
| 158 |
-void emit(tty_t *tty){
|
|
| 159 |
- /* ttyout should be flushed before outputting on stdout */ |
|
| 160 |
- fclose(tty->fout); |
|
| 161 |
- |
|
| 162 |
- if(choices_available){
|
|
| 101 |
+void emit(choices_t *choices){
|
|
| 102 |
+ const char *selection = choices_get(choices, choices->selection); |
|
| 103 |
+ if(selection){
|
|
| 163 | 104 |
/* output the selected result */ |
| 164 |
- printf("%s\n", choices[choices_sorted[current_selection]]);
|
|
| 105 |
+ printf("%s\n", selection);
|
|
| 165 | 106 |
}else{
|
| 166 | 107 |
/* No match, output the query instead */ |
| 167 | 108 |
printf("%s\n", search);
|
| ... | ... |
@@ -170,58 +111,54 @@ void emit(tty_t *tty){
|
| 170 | 111 |
exit(EXIT_SUCCESS); |
| 171 | 112 |
} |
| 172 | 113 |
|
| 173 |
-void action_prev(){
|
|
| 174 |
- current_selection = (current_selection + choices_available - 1) % choices_available; |
|
| 175 |
-} |
|
| 176 |
- |
|
| 177 |
-void action_next(){
|
|
| 178 |
- current_selection = (current_selection + 1) % choices_available; |
|
| 179 |
-} |
|
| 180 |
- |
|
| 181 |
-void run(tty_t *tty){
|
|
| 182 |
- run_search(search); |
|
| 114 |
+void run(tty_t *tty, choices_t *choices){
|
|
| 115 |
+ choices_search(choices, search); |
|
| 183 | 116 |
char ch; |
| 184 | 117 |
do {
|
| 185 |
- draw(tty); |
|
| 118 |
+ draw(tty, choices); |
|
| 186 | 119 |
ch = tty_getchar(tty); |
| 187 | 120 |
if(isprint(ch)){
|
| 188 | 121 |
if(search_size < SEARCH_SIZE_MAX){
|
| 189 | 122 |
search[search_size++] = ch; |
| 190 | 123 |
search[search_size] = '\0'; |
| 191 |
- run_search(search); |
|
| 124 |
+ choices_search(choices, search); |
|
| 192 | 125 |
} |
| 193 | 126 |
}else if(ch == 127 || ch == 8){ /* DEL || backspace */
|
| 194 | 127 |
if(search_size) |
| 195 | 128 |
search[--search_size] = '\0'; |
| 196 |
- run_search(search); |
|
| 129 |
+ choices_search(choices, search); |
|
| 197 | 130 |
}else if(ch == 21){ /* C-U */
|
| 198 | 131 |
search_size = 0; |
| 199 | 132 |
search[0] = '\0'; |
| 200 |
- run_search(search); |
|
| 133 |
+ choices_search(choices, search); |
|
| 201 | 134 |
}else if(ch == 23){ /* C-W */
|
| 202 | 135 |
if(search_size) |
| 203 | 136 |
search[--search_size] = '\0'; |
| 204 | 137 |
while(search_size && !isspace(search[--search_size])) |
| 205 | 138 |
search[search_size] = '\0'; |
| 206 |
- run_search(search); |
|
| 139 |
+ choices_search(choices, search); |
|
| 207 | 140 |
}else if(ch == 14){ /* C-N */
|
| 208 |
- action_next(); |
|
| 141 |
+ choices_next(choices); |
|
| 209 | 142 |
}else if(ch == 16){ /* C-P */
|
| 210 |
- action_prev(); |
|
| 143 |
+ choices_prev(choices); |
|
| 211 | 144 |
}else if(ch == 9){ /* TAB */
|
| 212 |
- strncpy(search, choices[choices_sorted[current_selection]], SEARCH_SIZE_MAX); |
|
| 145 |
+ strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
|
| 213 | 146 |
search_size = strlen(search); |
| 214 | 147 |
}else if(ch == 10){ /* Enter */
|
| 215 | 148 |
clear(tty); |
| 216 |
- emit(tty); |
|
| 149 |
+ |
|
| 150 |
+ /* ttyout should be flushed before outputting on stdout */ |
|
| 151 |
+ fclose(tty->fout); |
|
| 152 |
+ |
|
| 153 |
+ emit(choices); |
|
| 217 | 154 |
}else if(ch == 27){ /* ESC */
|
| 218 | 155 |
ch = tty_getchar(tty); |
| 219 | 156 |
if(ch == '[' || ch == 'O'){
|
| 220 | 157 |
ch = tty_getchar(tty); |
| 221 | 158 |
if(ch == 'A'){ /* UP ARROW */
|
| 222 |
- action_prev(); |
|
| 159 |
+ choices_prev(choices); |
|
| 223 | 160 |
}else if(ch == 'B'){ /* DOWN ARROW */
|
| 224 |
- action_next(); |
|
| 161 |
+ choices_next(choices); |
|
| 225 | 162 |
} |
| 226 | 163 |
} |
| 227 | 164 |
} |
| ... | ... |
@@ -297,8 +234,9 @@ int main(int argc, char *argv[]){
|
| 297 | 234 |
exit(EXIT_FAILURE); |
| 298 | 235 |
} |
| 299 | 236 |
|
| 300 |
- resize_choices(INITIAL_CAPACITY); |
|
| 301 |
- read_choices(); |
|
| 237 |
+ choices_t choices; |
|
| 238 |
+ choices_init(&choices); |
|
| 239 |
+ read_choices(&choices); |
|
| 302 | 240 |
|
| 303 | 241 |
if(benchmark){
|
| 304 | 242 |
if(!initial_query){
|
| ... | ... |
@@ -306,21 +244,20 @@ int main(int argc, char *argv[]){
|
| 306 | 244 |
exit(EXIT_FAILURE); |
| 307 | 245 |
} |
| 308 | 246 |
for(int i = 0; i < 100; i++) |
| 309 |
- run_search(initial_query); |
|
| 247 |
+ choices_search(&choices, initial_query); |
|
| 310 | 248 |
}else if(initial_query){
|
| 311 |
- run_search(initial_query); |
|
| 312 |
- for(size_t i = 0; i < choices_available; i++){
|
|
| 313 |
- size_t choice_idx = choices_sorted[i]; |
|
| 249 |
+ choices_search(&choices, initial_query); |
|
| 250 |
+ for(size_t i = 0; i < choices_available(&choices); i++){
|
|
| 314 | 251 |
if(flag_show_scores) |
| 315 |
- printf("%f\t", choices_score[choice_idx]);
|
|
| 316 |
- printf("%s\n", choices[choice_idx]);
|
|
| 252 |
+ printf("%f\t", choices_getscore(&choices, i));
|
|
| 253 |
+ printf("%s\n", choices_get(&choices, i));
|
|
| 317 | 254 |
} |
| 318 | 255 |
}else{
|
| 319 | 256 |
/* interactive */ |
| 320 | 257 |
tty_t tty; |
| 321 | 258 |
tty_init(&tty, tty_filename); |
| 322 | 259 |
|
| 323 |
- run(&tty); |
|
| 260 |
+ run(&tty, &choices); |
|
| 324 | 261 |
} |
| 325 | 262 |
|
| 326 | 263 |
return 0; |