| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,290 +0,0 @@ |
| 1 |
-#include <stdio.h> |
|
| 2 |
-#include <string.h> |
|
| 3 |
-#include <stdlib.h> |
|
| 4 |
-#include <ctype.h> |
|
| 5 |
-#include <getopt.h> |
|
| 6 |
-#include <limits.h> |
|
| 7 |
- |
|
| 8 |
-#include "match.h" |
|
| 9 |
-#include "tty.h" |
|
| 10 |
-#include "choices.h" |
|
| 11 |
- |
|
| 12 |
-#include "config.h" |
|
| 13 |
- |
|
| 14 |
-static int flag_show_scores = 0; |
|
| 15 |
- |
|
| 16 |
-static size_t num_lines = 10; |
|
| 17 |
-static size_t scrolloff = 1; |
|
| 18 |
- |
|
| 19 |
-static const char *prompt = "> "; |
|
| 20 |
- |
|
| 21 |
-#define SEARCH_SIZE_MAX 4096 |
|
| 22 |
-static char search[SEARCH_SIZE_MAX + 1] = {0};
|
|
| 23 |
- |
|
| 24 |
-static void clear(tty_t *tty) {
|
|
| 25 |
- tty_setcol(tty, 0); |
|
| 26 |
- size_t line = 0; |
|
| 27 |
- while (line++ < num_lines) {
|
|
| 28 |
- tty_newline(tty); |
|
| 29 |
- } |
|
| 30 |
- tty_clearline(tty); |
|
| 31 |
- tty_moveup(tty, line - 1); |
|
| 32 |
- tty_flush(tty); |
|
| 33 |
-} |
|
| 34 |
- |
|
| 35 |
-static void draw_match(tty_t *tty, const char *choice, int selected) {
|
|
| 36 |
- int n = strlen(search); |
|
| 37 |
- size_t positions[n + 1]; |
|
| 38 |
- for (int i = 0; i < n + 1; i++) |
|
| 39 |
- positions[i] = -1; |
|
| 40 |
- |
|
| 41 |
- double score = match_positions(search, choice, &positions[0]); |
|
| 42 |
- |
|
| 43 |
- size_t maxwidth = tty_getwidth(tty); |
|
| 44 |
- |
|
| 45 |
- if (flag_show_scores) |
|
| 46 |
- tty_printf(tty, "(%5.2f) ", score); |
|
| 47 |
- |
|
| 48 |
- if (selected) |
|
| 49 |
- tty_setinvert(tty); |
|
| 50 |
- |
|
| 51 |
- for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
|
|
| 52 |
- if (i + 1 < maxwidth) {
|
|
| 53 |
- if (positions[p] == i) {
|
|
| 54 |
- tty_setfg(tty, TTY_COLOR_HIGHLIGHT); |
|
| 55 |
- p++; |
|
| 56 |
- } else {
|
|
| 57 |
- tty_setfg(tty, TTY_COLOR_NORMAL); |
|
| 58 |
- } |
|
| 59 |
- tty_printf(tty, "%c", choice[i]); |
|
| 60 |
- } else {
|
|
| 61 |
- tty_printf(tty, "$"); |
|
| 62 |
- break; |
|
| 63 |
- } |
|
| 64 |
- } |
|
| 65 |
- tty_setnormal(tty); |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-static void draw(tty_t *tty, choices_t *choices) {
|
|
| 69 |
- size_t start = 0; |
|
| 70 |
- size_t current_selection = choices->selection; |
|
| 71 |
- if (current_selection + scrolloff >= num_lines) {
|
|
| 72 |
- start = current_selection + scrolloff - num_lines + 1; |
|
| 73 |
- if (start + num_lines >= choices_available(choices)) {
|
|
| 74 |
- start = choices_available(choices) - num_lines; |
|
| 75 |
- } |
|
| 76 |
- } |
|
| 77 |
- tty_setcol(tty, 0); |
|
| 78 |
- tty_printf(tty, "%s%s", prompt, search); |
|
| 79 |
- tty_clearline(tty); |
|
| 80 |
- for (size_t i = start; i < start + num_lines; i++) {
|
|
| 81 |
- tty_printf(tty, "\n"); |
|
| 82 |
- tty_clearline(tty); |
|
| 83 |
- const char *choice = choices_get(choices, i); |
|
| 84 |
- if (choice) {
|
|
| 85 |
- draw_match(tty, choice, i == choices->selection); |
|
| 86 |
- } |
|
| 87 |
- } |
|
| 88 |
- tty_moveup(tty, num_lines); |
|
| 89 |
- tty_setcol(tty, strlen(prompt) + strlen(search)); |
|
| 90 |
- tty_flush(tty); |
|
| 91 |
-} |
|
| 92 |
- |
|
| 93 |
-static void emit(choices_t *choices) {
|
|
| 94 |
- const char *selection = choices_get(choices, choices->selection); |
|
| 95 |
- if (selection) {
|
|
| 96 |
- /* output the selected result */ |
|
| 97 |
- printf("%s\n", selection);
|
|
| 98 |
- } else {
|
|
| 99 |
- /* No match, output the query instead */ |
|
| 100 |
- printf("%s\n", search);
|
|
| 101 |
- } |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-#define KEY_CTRL(key) ((key) - ('@'))
|
|
| 105 |
-#define KEY_DEL 127 |
|
| 106 |
-#define KEY_ESC 27 |
|
| 107 |
- |
|
| 108 |
-static void run(tty_t *tty, choices_t *choices) {
|
|
| 109 |
- choices_search(choices, search); |
|
| 110 |
- char ch; |
|
| 111 |
- do {
|
|
| 112 |
- draw(tty, choices); |
|
| 113 |
- ch = tty_getchar(tty); |
|
| 114 |
- size_t search_size = strlen(search); |
|
| 115 |
- if (isprint(ch)) {
|
|
| 116 |
- if (search_size < SEARCH_SIZE_MAX) {
|
|
| 117 |
- search[search_size++] = ch; |
|
| 118 |
- search[search_size] = '\0'; |
|
| 119 |
- choices_search(choices, search); |
|
| 120 |
- } |
|
| 121 |
- } else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
|
|
| 122 |
- if (search_size) |
|
| 123 |
- search[--search_size] = '\0'; |
|
| 124 |
- choices_search(choices, search); |
|
| 125 |
- } else if (ch == KEY_CTRL('U')) { /* C-U */
|
|
| 126 |
- search_size = 0; |
|
| 127 |
- search[0] = '\0'; |
|
| 128 |
- choices_search(choices, search); |
|
| 129 |
- } else if (ch == KEY_CTRL('W')) { /* C-W */
|
|
| 130 |
- if (search_size) |
|
| 131 |
- search[--search_size] = '\0'; |
|
| 132 |
- while (search_size && !isspace(search[--search_size])) |
|
| 133 |
- search[search_size] = '\0'; |
|
| 134 |
- choices_search(choices, search); |
|
| 135 |
- } else if (ch == KEY_CTRL('N')) { /* C-N */
|
|
| 136 |
- choices_next(choices); |
|
| 137 |
- } else if (ch == KEY_CTRL('P')) { /* C-P */
|
|
| 138 |
- choices_prev(choices); |
|
| 139 |
- } else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
|
|
| 140 |
- strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
|
| 141 |
- choices_search(choices, search); |
|
| 142 |
- } else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
|
|
| 143 |
- clear(tty); |
|
| 144 |
- tty_close(tty); |
|
| 145 |
- exit(EXIT_FAILURE); |
|
| 146 |
- } else if (ch == KEY_CTRL('M')) { /* CR */
|
|
| 147 |
- clear(tty); |
|
| 148 |
- |
|
| 149 |
- /* ttyout should be flushed before outputting on stdout */ |
|
| 150 |
- tty_close(tty); |
|
| 151 |
- |
|
| 152 |
- emit(choices); |
|
| 153 |
- |
|
| 154 |
- /* Return to eventually exit successfully */ |
|
| 155 |
- return; |
|
| 156 |
- } else if (ch == KEY_ESC) { /* ESC */
|
|
| 157 |
- ch = tty_getchar(tty); |
|
| 158 |
- if (ch == '[' || ch == 'O') {
|
|
| 159 |
- ch = tty_getchar(tty); |
|
| 160 |
- if (ch == 'A') { /* UP ARROW */
|
|
| 161 |
- choices_prev(choices); |
|
| 162 |
- } else if (ch == 'B') { /* DOWN ARROW */
|
|
| 163 |
- choices_next(choices); |
|
| 164 |
- } |
|
| 165 |
- } |
|
| 166 |
- } |
|
| 167 |
- } while (1); |
|
| 168 |
-} |
|
| 169 |
- |
|
| 170 |
-static const char *usage_str = |
|
| 171 |
- "" |
|
| 172 |
- "Usage: fzy [OPTION]...\n" |
|
| 173 |
- " -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
|
| 174 |
- " -p, --prompt=PROMPT Input prompt (default '> ')\n" |
|
| 175 |
- " -q, --query=QUERY Use QUERY as the initial search string\n" |
|
| 176 |
- " -e, --show-matches=QUERY Output the sorted matches of QUERY\n" |
|
| 177 |
- " -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
|
| 178 |
- " -s, --show-scores Show the scores of each match\n" |
|
| 179 |
- " -h, --help Display this help and exit\n" |
|
| 180 |
- " -v, --version Output version information and exit\n"; |
|
| 181 |
- |
|
| 182 |
-static void usage(const char *argv0) {
|
|
| 183 |
- fprintf(stderr, usage_str, argv0); |
|
| 184 |
-} |
|
| 185 |
- |
|
| 186 |
-static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'},
|
|
| 187 |
- {"query", required_argument, NULL, 'q'},
|
|
| 188 |
- {"lines", required_argument, NULL, 'l'},
|
|
| 189 |
- {"tty", required_argument, NULL, 't'},
|
|
| 190 |
- {"prompt", required_argument, NULL, 'p'},
|
|
| 191 |
- {"show-scores", no_argument, NULL, 's'},
|
|
| 192 |
- {"version", no_argument, NULL, 'v'},
|
|
| 193 |
- {"benchmark", optional_argument, NULL, 'b'},
|
|
| 194 |
- {"help", no_argument, NULL, 'h'},
|
|
| 195 |
- {NULL, 0, NULL, 0}};
|
|
| 196 |
- |
|
| 197 |
-int main(int argc, char *argv[]) {
|
|
| 198 |
- int benchmark = 0; |
|
| 199 |
- const char *filter = NULL; |
|
| 200 |
- const char *tty_filename = "/dev/tty"; |
|
| 201 |
- char c; |
|
| 202 |
- while ((c = getopt_long(argc, argv, "vhse:q:l:t:p:", longopts, NULL)) != -1) {
|
|
| 203 |
- switch (c) {
|
|
| 204 |
- case 'v': |
|
| 205 |
- printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
|
| 206 |
- exit(EXIT_SUCCESS); |
|
| 207 |
- case 's': |
|
| 208 |
- flag_show_scores = 1; |
|
| 209 |
- break; |
|
| 210 |
- case 'q': |
|
| 211 |
- strncpy(search, optarg, SEARCH_SIZE_MAX); |
|
| 212 |
- break; |
|
| 213 |
- case 'e': |
|
| 214 |
- filter = optarg; |
|
| 215 |
- break; |
|
| 216 |
- case 'b': |
|
| 217 |
- if (optarg) {
|
|
| 218 |
- if (sscanf(optarg, "%d", &benchmark) != 1) {
|
|
| 219 |
- usage(argv[0]); |
|
| 220 |
- exit(EXIT_FAILURE); |
|
| 221 |
- } |
|
| 222 |
- } else {
|
|
| 223 |
- benchmark = 100; |
|
| 224 |
- } |
|
| 225 |
- break; |
|
| 226 |
- case 't': |
|
| 227 |
- tty_filename = optarg; |
|
| 228 |
- break; |
|
| 229 |
- case 'p': |
|
| 230 |
- prompt = optarg; |
|
| 231 |
- break; |
|
| 232 |
- case 'l': {
|
|
| 233 |
- int l; |
|
| 234 |
- if (!strcmp(optarg, "max")) {
|
|
| 235 |
- l = INT_MAX; |
|
| 236 |
- } else if (sscanf(optarg, "%d", &l) != 1 || l < 3) {
|
|
| 237 |
- fprintf(stderr, "Invalid format for --lines: %s\n", optarg); |
|
| 238 |
- fprintf(stderr, "Must be integer in range 3..\n"); |
|
| 239 |
- usage(argv[0]); |
|
| 240 |
- exit(EXIT_FAILURE); |
|
| 241 |
- } |
|
| 242 |
- num_lines = l; |
|
| 243 |
- } break; |
|
| 244 |
- case 'h': |
|
| 245 |
- default: |
|
| 246 |
- usage(argv[0]); |
|
| 247 |
- exit(EXIT_SUCCESS); |
|
| 248 |
- } |
|
| 249 |
- } |
|
| 250 |
- if (optind != argc) {
|
|
| 251 |
- usage(argv[0]); |
|
| 252 |
- exit(EXIT_FAILURE); |
|
| 253 |
- } |
|
| 254 |
- |
|
| 255 |
- choices_t choices; |
|
| 256 |
- choices_init(&choices); |
|
| 257 |
- choices_fread(&choices, stdin); |
|
| 258 |
- |
|
| 259 |
- if (benchmark) {
|
|
| 260 |
- if (!filter) {
|
|
| 261 |
- fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
|
| 262 |
- exit(EXIT_FAILURE); |
|
| 263 |
- } |
|
| 264 |
- for (int i = 0; i < benchmark; i++) |
|
| 265 |
- choices_search(&choices, filter); |
|
| 266 |
- } else if (filter) {
|
|
| 267 |
- choices_search(&choices, filter); |
|
| 268 |
- for (size_t i = 0; i < choices_available(&choices); i++) {
|
|
| 269 |
- if (flag_show_scores) |
|
| 270 |
- printf("%f\t", choices_getscore(&choices, i));
|
|
| 271 |
- printf("%s\n", choices_get(&choices, i));
|
|
| 272 |
- } |
|
| 273 |
- } else {
|
|
| 274 |
- /* interactive */ |
|
| 275 |
- tty_t tty; |
|
| 276 |
- tty_init(&tty, tty_filename); |
|
| 277 |
- |
|
| 278 |
- if (num_lines > choices.size) |
|
| 279 |
- num_lines = choices.size; |
|
| 280 |
- |
|
| 281 |
- if (num_lines + 1 > tty_getheight(&tty)) |
|
| 282 |
- num_lines = tty_getheight(&tty) - 1; |
|
| 283 |
- |
|
| 284 |
- run(&tty, &choices); |
|
| 285 |
- } |
|
| 286 |
- |
|
| 287 |
- choices_destroy(&choices); |
|
| 288 |
- |
|
| 289 |
- return 0; |
|
| 290 |
-} |
| ... | ... |
@@ -256,9 +256,6 @@ int main(int argc, char *argv[]) {
|
| 256 | 256 |
choices_init(&choices); |
| 257 | 257 |
choices_fread(&choices, stdin); |
| 258 | 258 |
|
| 259 |
- if (num_lines > choices.size) |
|
| 260 |
- num_lines = choices.size; |
|
| 261 |
- |
|
| 262 | 259 |
if (benchmark) {
|
| 263 | 260 |
if (!filter) {
|
| 264 | 261 |
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
| ... | ... |
@@ -278,6 +275,9 @@ int main(int argc, char *argv[]) {
|
| 278 | 275 |
tty_t tty; |
| 279 | 276 |
tty_init(&tty, tty_filename); |
| 280 | 277 |
|
| 278 |
+ if (num_lines > choices.size) |
|
| 279 |
+ num_lines = choices.size; |
|
| 280 |
+ |
|
| 281 | 281 |
if (num_lines + 1 > tty_getheight(&tty)) |
| 282 | 282 |
num_lines = tty_getheight(&tty) - 1; |
| 283 | 283 |
|
This matches fzf's -q and selecta's -s
| ... | ... |
@@ -19,7 +19,6 @@ static size_t scrolloff = 1; |
| 19 | 19 |
static const char *prompt = "> "; |
| 20 | 20 |
|
| 21 | 21 |
#define SEARCH_SIZE_MAX 4096 |
| 22 |
-static size_t search_size; |
|
| 23 | 22 |
static char search[SEARCH_SIZE_MAX + 1] = {0};
|
| 24 | 23 |
|
| 25 | 24 |
static void clear(tty_t *tty) {
|
| ... | ... |
@@ -112,6 +111,7 @@ static void run(tty_t *tty, choices_t *choices) {
|
| 112 | 111 |
do {
|
| 113 | 112 |
draw(tty, choices); |
| 114 | 113 |
ch = tty_getchar(tty); |
| 114 |
+ size_t search_size = strlen(search); |
|
| 115 | 115 |
if (isprint(ch)) {
|
| 116 | 116 |
if (search_size < SEARCH_SIZE_MAX) {
|
| 117 | 117 |
search[search_size++] = ch; |
| ... | ... |
@@ -138,7 +138,6 @@ static void run(tty_t *tty, choices_t *choices) {
|
| 138 | 138 |
choices_prev(choices); |
| 139 | 139 |
} else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
|
| 140 | 140 |
strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
| 141 |
- search_size = strlen(search); |
|
| 142 | 141 |
choices_search(choices, search); |
| 143 | 142 |
} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
|
| 144 | 143 |
clear(tty); |
| ... | ... |
@@ -173,6 +172,7 @@ static const char *usage_str = |
| 173 | 172 |
"Usage: fzy [OPTION]...\n" |
| 174 | 173 |
" -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
| 175 | 174 |
" -p, --prompt=PROMPT Input prompt (default '> ')\n" |
| 175 |
+ " -q, --query=QUERY Use QUERY as the initial search string\n" |
|
| 176 | 176 |
" -e, --show-matches=QUERY Output the sorted matches of QUERY\n" |
| 177 | 177 |
" -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
| 178 | 178 |
" -s, --show-scores Show the scores of each match\n" |
| ... | ... |
@@ -184,6 +184,7 @@ static void usage(const char *argv0) {
|
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'},
|
| 187 |
+ {"query", required_argument, NULL, 'q'},
|
|
| 187 | 188 |
{"lines", required_argument, NULL, 'l'},
|
| 188 | 189 |
{"tty", required_argument, NULL, 't'},
|
| 189 | 190 |
{"prompt", required_argument, NULL, 'p'},
|
| ... | ... |
@@ -198,7 +199,7 @@ int main(int argc, char *argv[]) {
|
| 198 | 199 |
const char *filter = NULL; |
| 199 | 200 |
const char *tty_filename = "/dev/tty"; |
| 200 | 201 |
char c; |
| 201 |
- while ((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1) {
|
|
| 202 |
+ while ((c = getopt_long(argc, argv, "vhse:q:l:t:p:", longopts, NULL)) != -1) {
|
|
| 202 | 203 |
switch (c) {
|
| 203 | 204 |
case 'v': |
| 204 | 205 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| ... | ... |
@@ -206,6 +207,9 @@ int main(int argc, char *argv[]) {
|
| 206 | 207 |
case 's': |
| 207 | 208 |
flag_show_scores = 1; |
| 208 | 209 |
break; |
| 210 |
+ case 'q': |
|
| 211 |
+ strncpy(search, optarg, SEARCH_SIZE_MAX); |
|
| 212 |
+ break; |
|
| 209 | 213 |
case 'e': |
| 210 | 214 |
filter = optarg; |
| 211 | 215 |
break; |
More acurately represents what it really does.
| ... | ... |
@@ -195,7 +195,7 @@ static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'
|
| 195 | 195 |
|
| 196 | 196 |
int main(int argc, char *argv[]) {
|
| 197 | 197 |
int benchmark = 0; |
| 198 |
- const char *initial_query = NULL; |
|
| 198 |
+ const char *filter = NULL; |
|
| 199 | 199 |
const char *tty_filename = "/dev/tty"; |
| 200 | 200 |
char c; |
| 201 | 201 |
while ((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1) {
|
| ... | ... |
@@ -207,7 +207,7 @@ int main(int argc, char *argv[]) {
|
| 207 | 207 |
flag_show_scores = 1; |
| 208 | 208 |
break; |
| 209 | 209 |
case 'e': |
| 210 |
- initial_query = optarg; |
|
| 210 |
+ filter = optarg; |
|
| 211 | 211 |
break; |
| 212 | 212 |
case 'b': |
| 213 | 213 |
if (optarg) {
|
| ... | ... |
@@ -256,14 +256,14 @@ int main(int argc, char *argv[]) {
|
| 256 | 256 |
num_lines = choices.size; |
| 257 | 257 |
|
| 258 | 258 |
if (benchmark) {
|
| 259 |
- if (!initial_query) {
|
|
| 259 |
+ if (!filter) {
|
|
| 260 | 260 |
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
| 261 | 261 |
exit(EXIT_FAILURE); |
| 262 | 262 |
} |
| 263 | 263 |
for (int i = 0; i < benchmark; i++) |
| 264 |
- choices_search(&choices, initial_query); |
|
| 265 |
- } else if (initial_query) {
|
|
| 266 |
- choices_search(&choices, initial_query); |
|
| 264 |
+ choices_search(&choices, filter); |
|
| 265 |
+ } else if (filter) {
|
|
| 266 |
+ choices_search(&choices, filter); |
|
| 267 | 267 |
for (size_t i = 0; i < choices_available(&choices); i++) {
|
| 268 | 268 |
if (flag_show_scores) |
| 269 | 269 |
printf("%f\t", choices_getscore(&choices, i));
|
Previously we were failing to clear the very last line when exiting.
Thanks to Zaplanincan for reporting the issue.
No real advantage, but I get to feel warm and fuzzy about
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 8 allocs, 8 frees, 15,425 bytes allocated
| ... | ... |
@@ -99,8 +99,6 @@ static void emit(choices_t *choices) {
|
| 99 | 99 |
/* No match, output the query instead */ |
| 100 | 100 |
printf("%s\n", search);
|
| 101 | 101 |
} |
| 102 |
- |
|
| 103 |
- exit(EXIT_SUCCESS); |
|
| 104 | 102 |
} |
| 105 | 103 |
|
| 106 | 104 |
#define KEY_CTRL(key) ((key) - ('@'))
|
| ... | ... |
@@ -152,6 +150,9 @@ static void run(tty_t *tty, choices_t *choices) {
|
| 152 | 150 |
tty_close(tty); |
| 153 | 151 |
|
| 154 | 152 |
emit(choices); |
| 153 |
+ |
|
| 154 |
+ /* Return to eventually exit successfully */ |
|
| 155 |
+ return; |
|
| 155 | 156 |
} else if (ch == KEY_ESC) { /* ESC */
|
| 156 | 157 |
ch = tty_getchar(tty); |
| 157 | 158 |
if (ch == '[' || ch == 'O') {
|
Also refactored and added comments.
| ... | ... |
@@ -18,37 +18,6 @@ static size_t scrolloff = 1; |
| 18 | 18 |
|
| 19 | 19 |
static const char *prompt = "> "; |
| 20 | 20 |
|
| 21 |
-static void read_choices(choices_t *c) {
|
|
| 22 |
- size_t bufsize = 65536; |
|
| 23 |
- size_t pos = 0; |
|
| 24 |
- size_t sizeread; |
|
| 25 |
- |
|
| 26 |
- /* Read entire file into contiguous memory buffer */ |
|
| 27 |
- char *buf = malloc(bufsize); |
|
| 28 |
- while ((sizeread = fread(buf + pos, 1, bufsize - pos, stdin))) {
|
|
| 29 |
- pos += sizeread; |
|
| 30 |
- bufsize *= 2; |
|
| 31 |
- buf = realloc(buf, bufsize); |
|
| 32 |
- } |
|
| 33 |
- buf = realloc(buf, pos + 1); |
|
| 34 |
- |
|
| 35 |
- buf[pos] = 0; |
|
| 36 |
- |
|
| 37 |
- /* Tokenize input and add to choices */ |
|
| 38 |
- char *line = buf; |
|
| 39 |
- do {
|
|
| 40 |
- char *nl = strchr(line, '\n'); |
|
| 41 |
- if (nl) |
|
| 42 |
- *nl++ = '\0'; |
|
| 43 |
- |
|
| 44 |
- /* Skip empty lines */ |
|
| 45 |
- if (*line) |
|
| 46 |
- choices_add(c, line); |
|
| 47 |
- |
|
| 48 |
- line = nl; |
|
| 49 |
- } while (line); |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 | 21 |
#define SEARCH_SIZE_MAX 4096 |
| 53 | 22 |
static size_t search_size; |
| 54 | 23 |
static char search[SEARCH_SIZE_MAX + 1] = {0};
|
| ... | ... |
@@ -279,7 +248,7 @@ int main(int argc, char *argv[]) {
|
| 279 | 248 |
|
| 280 | 249 |
choices_t choices; |
| 281 | 250 |
choices_init(&choices); |
| 282 |
- read_choices(&choices); |
|
| 251 |
+ choices_fread(&choices, stdin); |
|
| 283 | 252 |
|
| 284 | 253 |
if (num_lines > choices.size) |
| 285 | 254 |
num_lines = choices.size; |
This should be slightly faster at loading the input into memory, since
fewer mallocs will be required vs running one malloc per-line of input.
Realloc on modern systems is fast. It can use mremap internally to remap
virtual memory pages and avoid copying. This can be seen in strace. Neat!
I thought that this might be friendlier to caching, but there was no
noticeable differnce when scanning through the strings for matches. It's
probable that the previous strdups placed them more-or-less contiguously
and the extra padding for malloc bookkeeping didn't make a significant
differnce. This also suggests that alignment of the candidate strings
wasn't important.
| ... | ... |
@@ -19,19 +19,34 @@ static size_t scrolloff = 1; |
| 19 | 19 |
static const char *prompt = "> "; |
| 20 | 20 |
|
| 21 | 21 |
static void read_choices(choices_t *c) {
|
| 22 |
- const char *line; |
|
| 23 |
- char buf[4096]; |
|
| 24 |
- while (fgets(buf, sizeof buf, stdin)) {
|
|
| 25 |
- char *nl; |
|
| 26 |
- if ((nl = strchr(buf, '\n'))) |
|
| 27 |
- *nl = '\0'; |
|
| 28 |
- |
|
| 29 |
- if (!(line = strdup(buf))) {
|
|
| 30 |
- fprintf(stderr, "Cannot allocate memory"); |
|
| 31 |
- abort(); |
|
| 32 |
- } |
|
| 33 |
- choices_add(c, line); |
|
| 22 |
+ size_t bufsize = 65536; |
|
| 23 |
+ size_t pos = 0; |
|
| 24 |
+ size_t sizeread; |
|
| 25 |
+ |
|
| 26 |
+ /* Read entire file into contiguous memory buffer */ |
|
| 27 |
+ char *buf = malloc(bufsize); |
|
| 28 |
+ while ((sizeread = fread(buf + pos, 1, bufsize - pos, stdin))) {
|
|
| 29 |
+ pos += sizeread; |
|
| 30 |
+ bufsize *= 2; |
|
| 31 |
+ buf = realloc(buf, bufsize); |
|
| 34 | 32 |
} |
| 33 |
+ buf = realloc(buf, pos + 1); |
|
| 34 |
+ |
|
| 35 |
+ buf[pos] = 0; |
|
| 36 |
+ |
|
| 37 |
+ /* Tokenize input and add to choices */ |
|
| 38 |
+ char *line = buf; |
|
| 39 |
+ do {
|
|
| 40 |
+ char *nl = strchr(line, '\n'); |
|
| 41 |
+ if (nl) |
|
| 42 |
+ *nl++ = '\0'; |
|
| 43 |
+ |
|
| 44 |
+ /* Skip empty lines */ |
|
| 45 |
+ if (*line) |
|
| 46 |
+ choices_add(c, line); |
|
| 47 |
+ |
|
| 48 |
+ line = nl; |
|
| 49 |
+ } while (line); |
|
| 35 | 50 |
} |
| 36 | 51 |
|
| 37 | 52 |
#define SEARCH_SIZE_MAX 4096 |
Prefers "magic letters" to magic numbers. It's easier to think of all
control codes as their shifted versions of letters A-Z in the ascii
table.
| ... | ... |
@@ -119,6 +119,10 @@ static void emit(choices_t *choices) {
|
| 119 | 119 |
exit(EXIT_SUCCESS); |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
+#define KEY_CTRL(key) ((key) - ('@'))
|
|
| 123 |
+#define KEY_DEL 127 |
|
| 124 |
+#define KEY_ESC 27 |
|
| 125 |
+ |
|
| 122 | 126 |
static void run(tty_t *tty, choices_t *choices) {
|
| 123 | 127 |
choices_search(choices, search); |
| 124 | 128 |
char ch; |
| ... | ... |
@@ -131,40 +135,40 @@ static void run(tty_t *tty, choices_t *choices) {
|
| 131 | 135 |
search[search_size] = '\0'; |
| 132 | 136 |
choices_search(choices, search); |
| 133 | 137 |
} |
| 134 |
- } else if (ch == 127 || ch == 8) { /* DEL || backspace */
|
|
| 138 |
+ } else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
|
|
| 135 | 139 |
if (search_size) |
| 136 | 140 |
search[--search_size] = '\0'; |
| 137 | 141 |
choices_search(choices, search); |
| 138 |
- } else if (ch == 21) { /* C-U */
|
|
| 142 |
+ } else if (ch == KEY_CTRL('U')) { /* C-U */
|
|
| 139 | 143 |
search_size = 0; |
| 140 | 144 |
search[0] = '\0'; |
| 141 | 145 |
choices_search(choices, search); |
| 142 |
- } else if (ch == 23) { /* C-W */
|
|
| 146 |
+ } else if (ch == KEY_CTRL('W')) { /* C-W */
|
|
| 143 | 147 |
if (search_size) |
| 144 | 148 |
search[--search_size] = '\0'; |
| 145 | 149 |
while (search_size && !isspace(search[--search_size])) |
| 146 | 150 |
search[search_size] = '\0'; |
| 147 | 151 |
choices_search(choices, search); |
| 148 |
- } else if (ch == 14) { /* C-N */
|
|
| 152 |
+ } else if (ch == KEY_CTRL('N')) { /* C-N */
|
|
| 149 | 153 |
choices_next(choices); |
| 150 |
- } else if (ch == 16) { /* C-P */
|
|
| 154 |
+ } else if (ch == KEY_CTRL('P')) { /* C-P */
|
|
| 151 | 155 |
choices_prev(choices); |
| 152 |
- } else if (ch == 9) { /* TAB */
|
|
| 156 |
+ } else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
|
|
| 153 | 157 |
strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
| 154 | 158 |
search_size = strlen(search); |
| 155 | 159 |
choices_search(choices, search); |
| 156 |
- } else if (ch == 3 || ch == 4) { /* ^C || ^D */
|
|
| 160 |
+ } else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
|
|
| 157 | 161 |
clear(tty); |
| 158 | 162 |
tty_close(tty); |
| 159 | 163 |
exit(EXIT_FAILURE); |
| 160 |
- } else if (ch == 13) { /* CR */
|
|
| 164 |
+ } else if (ch == KEY_CTRL('M')) { /* CR */
|
|
| 161 | 165 |
clear(tty); |
| 162 | 166 |
|
| 163 | 167 |
/* ttyout should be flushed before outputting on stdout */ |
| 164 | 168 |
tty_close(tty); |
| 165 | 169 |
|
| 166 | 170 |
emit(choices); |
| 167 |
- } else if (ch == 27) { /* ESC */
|
|
| 171 |
+ } else if (ch == KEY_ESC) { /* ESC */
|
|
| 168 | 172 |
ch = tty_getchar(tty); |
| 169 | 173 |
if (ch == '[' || ch == 'O') {
|
| 170 | 174 |
ch = tty_getchar(tty); |
I get a few reports of enter not working with fzy occasionally. This
usually occurrs after running a badly behaved program which doesn't
clean up the tty properly (looking at you, pry) after cleaning the
ICRNL flag.
This commit now always unsets ICRNL. This also could have been fixed by
ensuring ICRNL was set, but I believe this will give more control over
keybindings.
| ... | ... |
@@ -157,7 +157,7 @@ static void run(tty_t *tty, choices_t *choices) {
|
| 157 | 157 |
clear(tty); |
| 158 | 158 |
tty_close(tty); |
| 159 | 159 |
exit(EXIT_FAILURE); |
| 160 |
- } else if (ch == 10) { /* Enter */
|
|
| 160 |
+ } else if (ch == 13) { /* CR */
|
|
| 161 | 161 |
clear(tty); |
| 162 | 162 |
|
| 163 | 163 |
/* ttyout should be flushed before outputting on stdout */ |
| ... | ... |
@@ -221,12 +221,12 @@ int main(int argc, char *argv[]) {
|
| 221 | 221 |
break; |
| 222 | 222 |
case 'b': |
| 223 | 223 |
if (optarg) {
|
| 224 |
- if (sscanf(optarg, "%d", &benchmark) != 1) {
|
|
| 225 |
- usage(argv[0]); |
|
| 226 |
- exit(EXIT_FAILURE); |
|
| 227 |
- } |
|
| 224 |
+ if (sscanf(optarg, "%d", &benchmark) != 1) {
|
|
| 225 |
+ usage(argv[0]); |
|
| 226 |
+ exit(EXIT_FAILURE); |
|
| 227 |
+ } |
|
| 228 | 228 |
} else {
|
| 229 |
- benchmark = 100; |
|
| 229 |
+ benchmark = 100; |
|
| 230 | 230 |
} |
| 231 | 231 |
break; |
| 232 | 232 |
case 't': |
This is helpful because some searches are an order of magnitude easier
than others.
| ... | ... |
@@ -199,7 +199,7 @@ static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'
|
| 199 | 199 |
{"prompt", required_argument, NULL, 'p'},
|
| 200 | 200 |
{"show-scores", no_argument, NULL, 's'},
|
| 201 | 201 |
{"version", no_argument, NULL, 'v'},
|
| 202 |
- {"benchmark", no_argument, NULL, 'b'},
|
|
| 202 |
+ {"benchmark", optional_argument, NULL, 'b'},
|
|
| 203 | 203 |
{"help", no_argument, NULL, 'h'},
|
| 204 | 204 |
{NULL, 0, NULL, 0}};
|
| 205 | 205 |
|
| ... | ... |
@@ -220,7 +220,14 @@ int main(int argc, char *argv[]) {
|
| 220 | 220 |
initial_query = optarg; |
| 221 | 221 |
break; |
| 222 | 222 |
case 'b': |
| 223 |
- benchmark = 1; |
|
| 223 |
+ if (optarg) {
|
|
| 224 |
+ if (sscanf(optarg, "%d", &benchmark) != 1) {
|
|
| 225 |
+ usage(argv[0]); |
|
| 226 |
+ exit(EXIT_FAILURE); |
|
| 227 |
+ } |
|
| 228 |
+ } else {
|
|
| 229 |
+ benchmark = 100; |
|
| 230 |
+ } |
|
| 224 | 231 |
break; |
| 225 | 232 |
case 't': |
| 226 | 233 |
tty_filename = optarg; |
| ... | ... |
@@ -263,7 +270,7 @@ int main(int argc, char *argv[]) {
|
| 263 | 270 |
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
| 264 | 271 |
exit(EXIT_FAILURE); |
| 265 | 272 |
} |
| 266 |
- for (int i = 0; i < 100; i++) |
|
| 273 |
+ for (int i = 0; i < benchmark; i++) |
|
| 267 | 274 |
choices_search(&choices, initial_query); |
| 268 | 275 |
} else if (initial_query) {
|
| 269 | 276 |
choices_search(&choices, initial_query); |
| ... | ... |
@@ -11,14 +11,14 @@ |
| 11 | 11 |
|
| 12 | 12 |
#include "config.h" |
| 13 | 13 |
|
| 14 |
-int flag_show_scores = 0; |
|
| 14 |
+static int flag_show_scores = 0; |
|
| 15 | 15 |
|
| 16 |
-size_t num_lines = 10; |
|
| 17 |
-size_t scrolloff = 1; |
|
| 16 |
+static size_t num_lines = 10; |
|
| 17 |
+static size_t scrolloff = 1; |
|
| 18 | 18 |
|
| 19 |
-const char *prompt = "> "; |
|
| 19 |
+static const char *prompt = "> "; |
|
| 20 | 20 |
|
| 21 |
-void read_choices(choices_t *c) {
|
|
| 21 |
+static void read_choices(choices_t *c) {
|
|
| 22 | 22 |
const char *line; |
| 23 | 23 |
char buf[4096]; |
| 24 | 24 |
while (fgets(buf, sizeof buf, stdin)) {
|
| ... | ... |
@@ -35,10 +35,10 @@ void read_choices(choices_t *c) {
|
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 | 37 |
#define SEARCH_SIZE_MAX 4096 |
| 38 |
-size_t search_size; |
|
| 39 |
-char search[SEARCH_SIZE_MAX + 1] = {0};
|
|
| 38 |
+static size_t search_size; |
|
| 39 |
+static char search[SEARCH_SIZE_MAX + 1] = {0};
|
|
| 40 | 40 |
|
| 41 |
-void clear(tty_t *tty) {
|
|
| 41 |
+static void clear(tty_t *tty) {
|
|
| 42 | 42 |
tty_setcol(tty, 0); |
| 43 | 43 |
size_t line = 0; |
| 44 | 44 |
while (line++ < num_lines) {
|
| ... | ... |
@@ -48,7 +48,7 @@ void clear(tty_t *tty) {
|
| 48 | 48 |
tty_flush(tty); |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
-void draw_match(tty_t *tty, const char *choice, int selected) {
|
|
| 51 |
+static void draw_match(tty_t *tty, const char *choice, int selected) {
|
|
| 52 | 52 |
int n = strlen(search); |
| 53 | 53 |
size_t positions[n + 1]; |
| 54 | 54 |
for (int i = 0; i < n + 1; i++) |
| ... | ... |
@@ -81,7 +81,7 @@ void draw_match(tty_t *tty, const char *choice, int selected) {
|
| 81 | 81 |
tty_setnormal(tty); |
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 |
-void draw(tty_t *tty, choices_t *choices) {
|
|
| 84 |
+static void draw(tty_t *tty, choices_t *choices) {
|
|
| 85 | 85 |
size_t start = 0; |
| 86 | 86 |
size_t current_selection = choices->selection; |
| 87 | 87 |
if (current_selection + scrolloff >= num_lines) {
|
| ... | ... |
@@ -106,7 +106,7 @@ void draw(tty_t *tty, choices_t *choices) {
|
| 106 | 106 |
tty_flush(tty); |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
-void emit(choices_t *choices) {
|
|
| 109 |
+static void emit(choices_t *choices) {
|
|
| 110 | 110 |
const char *selection = choices_get(choices, choices->selection); |
| 111 | 111 |
if (selection) {
|
| 112 | 112 |
/* output the selected result */ |
| ... | ... |
@@ -119,7 +119,7 @@ void emit(choices_t *choices) {
|
| 119 | 119 |
exit(EXIT_SUCCESS); |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
-void run(tty_t *tty, choices_t *choices) {
|
|
| 122 |
+static void run(tty_t *tty, choices_t *choices) {
|
|
| 123 | 123 |
choices_search(choices, search); |
| 124 | 124 |
char ch; |
| 125 | 125 |
do {
|
| ... | ... |
@@ -189,7 +189,7 @@ static const char *usage_str = |
| 189 | 189 |
" -h, --help Display this help and exit\n" |
| 190 | 190 |
" -v, --version Output version information and exit\n"; |
| 191 | 191 |
|
| 192 |
-void usage(const char *argv0) {
|
|
| 192 |
+static void usage(const char *argv0) {
|
|
| 193 | 193 |
fprintf(stderr, usage_str, argv0); |
| 194 | 194 |
} |
| 195 | 195 |
|
Apologies that this uses my preferred formatting style: mostly the same
as Linux, but without a break between function and brace. Adds spaces in
a few places they weren't before.
| ... | ... |
@@ -18,15 +18,15 @@ size_t scrolloff = 1; |
| 18 | 18 |
|
| 19 | 19 |
const char *prompt = "> "; |
| 20 | 20 |
|
| 21 |
-void read_choices(choices_t *c){
|
|
| 21 |
+void read_choices(choices_t *c) {
|
|
| 22 | 22 |
const char *line; |
| 23 | 23 |
char buf[4096]; |
| 24 |
- while(fgets(buf, sizeof buf, stdin)){
|
|
| 24 |
+ while (fgets(buf, sizeof buf, stdin)) {
|
|
| 25 | 25 |
char *nl; |
| 26 |
- if((nl = strchr(buf, '\n'))) |
|
| 26 |
+ if ((nl = strchr(buf, '\n'))) |
|
| 27 | 27 |
*nl = '\0'; |
| 28 | 28 |
|
| 29 |
- if(!(line = strdup(buf))){
|
|
| 29 |
+ if (!(line = strdup(buf))) {
|
|
| 30 | 30 |
fprintf(stderr, "Cannot allocate memory"); |
| 31 | 31 |
abort(); |
| 32 | 32 |
} |
| ... | ... |
@@ -38,42 +38,42 @@ void read_choices(choices_t *c){
|
| 38 | 38 |
int search_size; |
| 39 | 39 |
char search[SEARCH_SIZE_MAX + 1] = {0};
|
| 40 | 40 |
|
| 41 |
-void clear(tty_t *tty){
|
|
| 41 |
+void clear(tty_t *tty) {
|
|
| 42 | 42 |
tty_setcol(tty, 0); |
| 43 | 43 |
size_t line = 0; |
| 44 |
- while(line++ < num_lines){
|
|
| 44 |
+ while (line++ < num_lines) {
|
|
| 45 | 45 |
tty_newline(tty); |
| 46 | 46 |
} |
| 47 |
- tty_moveup(tty, line-1); |
|
| 47 |
+ tty_moveup(tty, line - 1); |
|
| 48 | 48 |
tty_flush(tty); |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
-void draw_match(tty_t *tty, const char *choice, int selected){
|
|
| 51 |
+void draw_match(tty_t *tty, const char *choice, int selected) {
|
|
| 52 | 52 |
int n = strlen(search); |
| 53 | 53 |
size_t positions[n + 1]; |
| 54 |
- for(int i = 0; i < n + 1; i++) |
|
| 54 |
+ for (int i = 0; i < n + 1; i++) |
|
| 55 | 55 |
positions[i] = -1; |
| 56 | 56 |
|
| 57 | 57 |
double score = match_positions(search, choice, &positions[0]); |
| 58 | 58 |
|
| 59 | 59 |
size_t maxwidth = tty_getwidth(tty); |
| 60 | 60 |
|
| 61 |
- if(flag_show_scores) |
|
| 61 |
+ if (flag_show_scores) |
|
| 62 | 62 |
tty_printf(tty, "(%5.2f) ", score); |
| 63 | 63 |
|
| 64 |
- if(selected) |
|
| 64 |
+ if (selected) |
|
| 65 | 65 |
tty_setinvert(tty); |
| 66 | 66 |
|
| 67 |
- for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
|
| 68 |
- if(i+1 < maxwidth){
|
|
| 69 |
- if(positions[p] == i){
|
|
| 67 |
+ for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
|
|
| 68 |
+ if (i + 1 < maxwidth) {
|
|
| 69 |
+ if (positions[p] == i) {
|
|
| 70 | 70 |
tty_setfg(tty, TTY_COLOR_HIGHLIGHT); |
| 71 | 71 |
p++; |
| 72 |
- }else{
|
|
| 72 |
+ } else {
|
|
| 73 | 73 |
tty_setfg(tty, TTY_COLOR_NORMAL); |
| 74 | 74 |
} |
| 75 | 75 |
tty_printf(tty, "%c", choice[i]); |
| 76 |
- }else{
|
|
| 76 |
+ } else {
|
|
| 77 | 77 |
tty_printf(tty, "$"); |
| 78 | 78 |
break; |
| 79 | 79 |
} |
| ... | ... |
@@ -81,23 +81,23 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 81 | 81 |
tty_setnormal(tty); |
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 |
-void draw(tty_t *tty, choices_t *choices){
|
|
| 84 |
+void draw(tty_t *tty, choices_t *choices) {
|
|
| 85 | 85 |
size_t start = 0; |
| 86 | 86 |
size_t current_selection = choices->selection; |
| 87 |
- if(current_selection + scrolloff >= num_lines){
|
|
| 87 |
+ if (current_selection + scrolloff >= num_lines) {
|
|
| 88 | 88 |
start = current_selection + scrolloff - num_lines + 1; |
| 89 |
- if(start + num_lines >= choices_available(choices)){
|
|
| 89 |
+ if (start + num_lines >= choices_available(choices)) {
|
|
| 90 | 90 |
start = choices_available(choices) - num_lines; |
| 91 | 91 |
} |
| 92 | 92 |
} |
| 93 | 93 |
tty_setcol(tty, 0); |
| 94 | 94 |
tty_printf(tty, "%s%s", prompt, search); |
| 95 | 95 |
tty_clearline(tty); |
| 96 |
- for(size_t i = start; i < start + num_lines; i++){
|
|
| 96 |
+ for (size_t i = start; i < start + num_lines; i++) {
|
|
| 97 | 97 |
tty_printf(tty, "\n"); |
| 98 | 98 |
tty_clearline(tty); |
| 99 | 99 |
const char *choice = choices_get(choices, i); |
| 100 |
- if(choice){
|
|
| 100 |
+ if (choice) {
|
|
| 101 | 101 |
draw_match(tty, choice, i == choices->selection); |
| 102 | 102 |
} |
| 103 | 103 |
} |
| ... | ... |
@@ -106,12 +106,12 @@ void draw(tty_t *tty, choices_t *choices){
|
| 106 | 106 |
tty_flush(tty); |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
-void emit(choices_t *choices){
|
|
| 109 |
+void emit(choices_t *choices) {
|
|
| 110 | 110 |
const char *selection = choices_get(choices, choices->selection); |
| 111 |
- if(selection){
|
|
| 111 |
+ if (selection) {
|
|
| 112 | 112 |
/* output the selected result */ |
| 113 | 113 |
printf("%s\n", selection);
|
| 114 |
- }else{
|
|
| 114 |
+ } else {
|
|
| 115 | 115 |
/* No match, output the query instead */ |
| 116 | 116 |
printf("%s\n", search);
|
| 117 | 117 |
} |
| ... | ... |
@@ -119,98 +119,97 @@ void emit(choices_t *choices){
|
| 119 | 119 |
exit(EXIT_SUCCESS); |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
-void run(tty_t *tty, choices_t *choices){
|
|
| 122 |
+void run(tty_t *tty, choices_t *choices) {
|
|
| 123 | 123 |
choices_search(choices, search); |
| 124 | 124 |
char ch; |
| 125 | 125 |
do {
|
| 126 | 126 |
draw(tty, choices); |
| 127 | 127 |
ch = tty_getchar(tty); |
| 128 |
- if(isprint(ch)){
|
|
| 129 |
- if(search_size < SEARCH_SIZE_MAX){
|
|
| 128 |
+ if (isprint(ch)) {
|
|
| 129 |
+ if (search_size < SEARCH_SIZE_MAX) {
|
|
| 130 | 130 |
search[search_size++] = ch; |
| 131 | 131 |
search[search_size] = '\0'; |
| 132 | 132 |
choices_search(choices, search); |
| 133 | 133 |
} |
| 134 |
- }else if(ch == 127 || ch == 8){ /* DEL || backspace */
|
|
| 135 |
- if(search_size) |
|
| 134 |
+ } else if (ch == 127 || ch == 8) { /* DEL || backspace */
|
|
| 135 |
+ if (search_size) |
|
| 136 | 136 |
search[--search_size] = '\0'; |
| 137 | 137 |
choices_search(choices, search); |
| 138 |
- }else if(ch == 21){ /* C-U */
|
|
| 138 |
+ } else if (ch == 21) { /* C-U */
|
|
| 139 | 139 |
search_size = 0; |
| 140 | 140 |
search[0] = '\0'; |
| 141 | 141 |
choices_search(choices, search); |
| 142 |
- }else if(ch == 23){ /* C-W */
|
|
| 143 |
- if(search_size) |
|
| 142 |
+ } else if (ch == 23) { /* C-W */
|
|
| 143 |
+ if (search_size) |
|
| 144 | 144 |
search[--search_size] = '\0'; |
| 145 |
- while(search_size && !isspace(search[--search_size])) |
|
| 145 |
+ while (search_size && !isspace(search[--search_size])) |
|
| 146 | 146 |
search[search_size] = '\0'; |
| 147 | 147 |
choices_search(choices, search); |
| 148 |
- }else if(ch == 14){ /* C-N */
|
|
| 148 |
+ } else if (ch == 14) { /* C-N */
|
|
| 149 | 149 |
choices_next(choices); |
| 150 |
- }else if(ch == 16){ /* C-P */
|
|
| 150 |
+ } else if (ch == 16) { /* C-P */
|
|
| 151 | 151 |
choices_prev(choices); |
| 152 |
- }else if(ch == 9){ /* TAB */
|
|
| 152 |
+ } else if (ch == 9) { /* TAB */
|
|
| 153 | 153 |
strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
| 154 | 154 |
search_size = strlen(search); |
| 155 | 155 |
choices_search(choices, search); |
| 156 |
- }else if(ch == 3 || ch == 4){ /* ^C || ^D */
|
|
| 156 |
+ } else if (ch == 3 || ch == 4) { /* ^C || ^D */
|
|
| 157 | 157 |
clear(tty); |
| 158 | 158 |
tty_close(tty); |
| 159 | 159 |
exit(EXIT_FAILURE); |
| 160 |
- }else if(ch == 10){ /* Enter */
|
|
| 160 |
+ } else if (ch == 10) { /* Enter */
|
|
| 161 | 161 |
clear(tty); |
| 162 | 162 |
|
| 163 | 163 |
/* ttyout should be flushed before outputting on stdout */ |
| 164 | 164 |
tty_close(tty); |
| 165 | 165 |
|
| 166 | 166 |
emit(choices); |
| 167 |
- }else if(ch == 27){ /* ESC */
|
|
| 167 |
+ } else if (ch == 27) { /* ESC */
|
|
| 168 | 168 |
ch = tty_getchar(tty); |
| 169 |
- if(ch == '[' || ch == 'O'){
|
|
| 169 |
+ if (ch == '[' || ch == 'O') {
|
|
| 170 | 170 |
ch = tty_getchar(tty); |
| 171 |
- if(ch == 'A'){ /* UP ARROW */
|
|
| 171 |
+ if (ch == 'A') { /* UP ARROW */
|
|
| 172 | 172 |
choices_prev(choices); |
| 173 |
- }else if(ch == 'B'){ /* DOWN ARROW */
|
|
| 173 |
+ } else if (ch == 'B') { /* DOWN ARROW */
|
|
| 174 | 174 |
choices_next(choices); |
| 175 | 175 |
} |
| 176 | 176 |
} |
| 177 | 177 |
} |
| 178 |
- }while(1); |
|
| 178 |
+ } while (1); |
|
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
-static const char *usage_str = "" |
|
| 182 |
-"Usage: fzy [OPTION]...\n" |
|
| 183 |
-" -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
|
| 184 |
-" -p, --prompt=PROMPT Input prompt (default '> ')\n" |
|
| 185 |
-" -e, --show-matches=QUERY Output the sorted matches of QUERY\n" |
|
| 186 |
-" -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
|
| 187 |
-" -s, --show-scores Show the scores of each match\n" |
|
| 188 |
-" -h, --help Display this help and exit\n" |
|
| 189 |
-" -v, --version Output version information and exit\n"; |
|
| 181 |
+static const char *usage_str = |
|
| 182 |
+ "" |
|
| 183 |
+ "Usage: fzy [OPTION]...\n" |
|
| 184 |
+ " -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
|
| 185 |
+ " -p, --prompt=PROMPT Input prompt (default '> ')\n" |
|
| 186 |
+ " -e, --show-matches=QUERY Output the sorted matches of QUERY\n" |
|
| 187 |
+ " -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
|
| 188 |
+ " -s, --show-scores Show the scores of each match\n" |
|
| 189 |
+ " -h, --help Display this help and exit\n" |
|
| 190 |
+ " -v, --version Output version information and exit\n"; |
|
| 190 | 191 |
|
| 191 |
-void usage(const char *argv0){
|
|
| 192 |
+void usage(const char *argv0) {
|
|
| 192 | 193 |
fprintf(stderr, usage_str, argv0); |
| 193 | 194 |
} |
| 194 | 195 |
|
| 195 |
-static struct option longopts[] = {
|
|
| 196 |
- { "show-matches", required_argument, NULL, 'e' },
|
|
| 197 |
- { "lines", required_argument, NULL, 'l' },
|
|
| 198 |
- { "tty", required_argument, NULL, 't' },
|
|
| 199 |
- { "prompt", required_argument, NULL, 'p' },
|
|
| 200 |
- { "show-scores", no_argument, NULL, 's' },
|
|
| 201 |
- { "version", no_argument, NULL, 'v' },
|
|
| 202 |
- { "benchmark", no_argument, NULL, 'b' },
|
|
| 203 |
- { "help", no_argument, NULL, 'h' },
|
|
| 204 |
- { NULL, 0, NULL, 0 }
|
|
| 205 |
-}; |
|
| 196 |
+static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'},
|
|
| 197 |
+ {"lines", required_argument, NULL, 'l'},
|
|
| 198 |
+ {"tty", required_argument, NULL, 't'},
|
|
| 199 |
+ {"prompt", required_argument, NULL, 'p'},
|
|
| 200 |
+ {"show-scores", no_argument, NULL, 's'},
|
|
| 201 |
+ {"version", no_argument, NULL, 'v'},
|
|
| 202 |
+ {"benchmark", no_argument, NULL, 'b'},
|
|
| 203 |
+ {"help", no_argument, NULL, 'h'},
|
|
| 204 |
+ {NULL, 0, NULL, 0}};
|
|
| 206 | 205 |
|
| 207 |
-int main(int argc, char *argv[]){
|
|
| 206 |
+int main(int argc, char *argv[]) {
|
|
| 208 | 207 |
int benchmark = 0; |
| 209 | 208 |
const char *initial_query = NULL; |
| 210 | 209 |
const char *tty_filename = "/dev/tty"; |
| 211 | 210 |
char c; |
| 212 |
- while((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1){
|
|
| 213 |
- switch(c){
|
|
| 211 |
+ while ((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1) {
|
|
| 212 |
+ switch (c) {
|
|
| 214 | 213 |
case 'v': |
| 215 | 214 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| 216 | 215 |
exit(EXIT_SUCCESS); |
| ... | ... |
@@ -229,27 +228,25 @@ int main(int argc, char *argv[]){
|
| 229 | 228 |
case 'p': |
| 230 | 229 |
prompt = optarg; |
| 231 | 230 |
break; |
| 232 |
- case 'l': |
|
| 233 |
- {
|
|
| 234 |
- int l; |
|
| 235 |
- if(!strcmp(optarg, "max")){
|
|
| 236 |
- l = INT_MAX; |
|
| 237 |
- }else if(sscanf(optarg, "%d", &l) != 1 || l < 3){
|
|
| 238 |
- fprintf(stderr, "Invalid format for --lines: %s\n", optarg); |
|
| 239 |
- fprintf(stderr, "Must be integer in range 3..\n"); |
|
| 240 |
- usage(argv[0]); |
|
| 241 |
- exit(EXIT_FAILURE); |
|
| 242 |
- } |
|
| 243 |
- num_lines = l; |
|
| 231 |
+ case 'l': {
|
|
| 232 |
+ int l; |
|
| 233 |
+ if (!strcmp(optarg, "max")) {
|
|
| 234 |
+ l = INT_MAX; |
|
| 235 |
+ } else if (sscanf(optarg, "%d", &l) != 1 || l < 3) {
|
|
| 236 |
+ fprintf(stderr, "Invalid format for --lines: %s\n", optarg); |
|
| 237 |
+ fprintf(stderr, "Must be integer in range 3..\n"); |
|
| 238 |
+ usage(argv[0]); |
|
| 239 |
+ exit(EXIT_FAILURE); |
|
| 244 | 240 |
} |
| 245 |
- break; |
|
| 241 |
+ num_lines = l; |
|
| 242 |
+ } break; |
|
| 246 | 243 |
case 'h': |
| 247 | 244 |
default: |
| 248 | 245 |
usage(argv[0]); |
| 249 | 246 |
exit(EXIT_SUCCESS); |
| 250 | 247 |
} |
| 251 | 248 |
} |
| 252 |
- if(optind != argc){
|
|
| 249 |
+ if (optind != argc) {
|
|
| 253 | 250 |
usage(argv[0]); |
| 254 | 251 |
exit(EXIT_FAILURE); |
| 255 | 252 |
} |
| ... | ... |
@@ -258,29 +255,29 @@ int main(int argc, char *argv[]){
|
| 258 | 255 |
choices_init(&choices); |
| 259 | 256 |
read_choices(&choices); |
| 260 | 257 |
|
| 261 |
- if(num_lines > choices.size) |
|
| 258 |
+ if (num_lines > choices.size) |
|
| 262 | 259 |
num_lines = choices.size; |
| 263 | 260 |
|
| 264 |
- if(benchmark){
|
|
| 265 |
- if(!initial_query){
|
|
| 261 |
+ if (benchmark) {
|
|
| 262 |
+ if (!initial_query) {
|
|
| 266 | 263 |
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
| 267 | 264 |
exit(EXIT_FAILURE); |
| 268 | 265 |
} |
| 269 |
- for(int i = 0; i < 100; i++) |
|
| 266 |
+ for (int i = 0; i < 100; i++) |
|
| 270 | 267 |
choices_search(&choices, initial_query); |
| 271 |
- }else if(initial_query){
|
|
| 268 |
+ } else if (initial_query) {
|
|
| 272 | 269 |
choices_search(&choices, initial_query); |
| 273 |
- for(size_t i = 0; i < choices_available(&choices); i++){
|
|
| 274 |
- if(flag_show_scores) |
|
| 270 |
+ for (size_t i = 0; i < choices_available(&choices); i++) {
|
|
| 271 |
+ if (flag_show_scores) |
|
| 275 | 272 |
printf("%f\t", choices_getscore(&choices, i));
|
| 276 | 273 |
printf("%s\n", choices_get(&choices, i));
|
| 277 | 274 |
} |
| 278 |
- }else{
|
|
| 275 |
+ } else {
|
|
| 279 | 276 |
/* interactive */ |
| 280 | 277 |
tty_t tty; |
| 281 | 278 |
tty_init(&tty, tty_filename); |
| 282 | 279 |
|
| 283 |
- if(num_lines + 1 > tty_getheight(&tty)) |
|
| 280 |
+ if (num_lines + 1 > tty_getheight(&tty)) |
|
| 284 | 281 |
num_lines = tty_getheight(&tty) - 1; |
| 285 | 282 |
|
| 286 | 283 |
run(&tty, &choices); |
Allows us to interpret ^C through ^Z as control codes and handle them
properly.
Also fixes resetting termios after ^C
| ... | ... |
@@ -153,6 +153,10 @@ void run(tty_t *tty, choices_t *choices){
|
| 153 | 153 |
strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
| 154 | 154 |
search_size = strlen(search); |
| 155 | 155 |
choices_search(choices, search); |
| 156 |
+ }else if(ch == 3 || ch == 4){ /* ^C || ^D */
|
|
| 157 |
+ clear(tty); |
|
| 158 |
+ tty_close(tty); |
|
| 159 |
+ exit(EXIT_FAILURE); |
|
| 156 | 160 |
}else if(ch == 10){ /* Enter */
|
| 157 | 161 |
clear(tty); |
| 158 | 162 |
|
Still needs to be fixed on an exit due to ^C
Thanks @rtandy
getline is a little greedy with memory usage, using a little extra for
every line read. strdup should always use the minimum amount.
| ... | ... |
@@ -19,20 +19,19 @@ size_t scrolloff = 1; |
| 19 | 19 |
const char *prompt = "> "; |
| 20 | 20 |
|
| 21 | 21 |
void read_choices(choices_t *c){
|
| 22 |
- char *line = NULL; |
|
| 23 |
- size_t len = 0; |
|
| 24 |
- ssize_t read; |
|
| 25 |
- |
|
| 26 |
- while ((read = getline(&line, &len, stdin)) != -1) {
|
|
| 22 |
+ const char *line; |
|
| 23 |
+ char buf[4096]; |
|
| 24 |
+ while(fgets(buf, sizeof buf, stdin)){
|
|
| 27 | 25 |
char *nl; |
| 28 |
- if((nl = strchr(line, '\n'))) |
|
| 26 |
+ if((nl = strchr(buf, '\n'))) |
|
| 29 | 27 |
*nl = '\0'; |
| 30 | 28 |
|
| 29 |
+ if(!(line = strdup(buf))){
|
|
| 30 |
+ fprintf(stderr, "Cannot allocate memory"); |
|
| 31 |
+ abort(); |
|
| 32 |
+ } |
|
| 31 | 33 |
choices_add(c, line); |
| 32 |
- |
|
| 33 |
- line = NULL; |
|
| 34 | 34 |
} |
| 35 |
- free(line); |
|
| 36 | 35 |
} |
| 37 | 36 |
|
| 38 | 37 |
#define SEARCH_SIZE_MAX 4096 |
| ... | ... |
@@ -203,8 +203,8 @@ static struct option longopts[] = {
|
| 203 | 203 |
|
| 204 | 204 |
int main(int argc, char *argv[]){
|
| 205 | 205 |
int benchmark = 0; |
| 206 |
- char *initial_query = NULL; |
|
| 207 |
- char *tty_filename = "/dev/tty"; |
|
| 206 |
+ const char *initial_query = NULL; |
|
| 207 |
+ const char *tty_filename = "/dev/tty"; |
|
| 208 | 208 |
char c; |
| 209 | 209 |
while((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1){
|
| 210 | 210 |
switch(c){
|
| ... | ... |
@@ -16,6 +16,8 @@ int flag_show_scores = 0; |
| 16 | 16 |
size_t num_lines = 10; |
| 17 | 17 |
size_t scrolloff = 1; |
| 18 | 18 |
|
| 19 |
+const char *prompt = "> "; |
|
| 20 |
+ |
|
| 19 | 21 |
void read_choices(choices_t *c){
|
| 20 | 22 |
char *line = NULL; |
| 21 | 23 |
size_t len = 0; |
| ... | ... |
@@ -89,7 +91,6 @@ void draw(tty_t *tty, choices_t *choices){
|
| 89 | 91 |
start = choices_available(choices) - num_lines; |
| 90 | 92 |
} |
| 91 | 93 |
} |
| 92 |
- const char *prompt = "> "; |
|
| 93 | 94 |
tty_setcol(tty, 0); |
| 94 | 95 |
tty_printf(tty, "%s%s", prompt, search); |
| 95 | 96 |
tty_clearline(tty); |
| ... | ... |
@@ -177,11 +178,12 @@ void run(tty_t *tty, choices_t *choices){
|
| 177 | 178 |
static const char *usage_str = "" |
| 178 | 179 |
"Usage: fzy [OPTION]...\n" |
| 179 | 180 |
" -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
| 180 |
-" -e, --show-matches=QUERY output the sorted matches of QUERY\n" |
|
| 181 |
+" -p, --prompt=PROMPT Input prompt (default '> ')\n" |
|
| 182 |
+" -e, --show-matches=QUERY Output the sorted matches of QUERY\n" |
|
| 181 | 183 |
" -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
| 182 |
-" -s, --show-scores show the scores of each match\n" |
|
| 183 |
-" -h, --help display this help and exit\n" |
|
| 184 |
-" -v, --version output version information and exit\n"; |
|
| 184 |
+" -s, --show-scores Show the scores of each match\n" |
|
| 185 |
+" -h, --help Display this help and exit\n" |
|
| 186 |
+" -v, --version Output version information and exit\n"; |
|
| 185 | 187 |
|
| 186 | 188 |
void usage(const char *argv0){
|
| 187 | 189 |
fprintf(stderr, usage_str, argv0); |
| ... | ... |
@@ -191,6 +193,7 @@ static struct option longopts[] = {
|
| 191 | 193 |
{ "show-matches", required_argument, NULL, 'e' },
|
| 192 | 194 |
{ "lines", required_argument, NULL, 'l' },
|
| 193 | 195 |
{ "tty", required_argument, NULL, 't' },
|
| 196 |
+ { "prompt", required_argument, NULL, 'p' },
|
|
| 194 | 197 |
{ "show-scores", no_argument, NULL, 's' },
|
| 195 | 198 |
{ "version", no_argument, NULL, 'v' },
|
| 196 | 199 |
{ "benchmark", no_argument, NULL, 'b' },
|
| ... | ... |
@@ -203,7 +206,7 @@ int main(int argc, char *argv[]){
|
| 203 | 206 |
char *initial_query = NULL; |
| 204 | 207 |
char *tty_filename = "/dev/tty"; |
| 205 | 208 |
char c; |
| 206 |
- while((c = getopt_long(argc, argv, "vhse:l:t:", longopts, NULL)) != -1){
|
|
| 209 |
+ while((c = getopt_long(argc, argv, "vhse:l:t:p:", longopts, NULL)) != -1){
|
|
| 207 | 210 |
switch(c){
|
| 208 | 211 |
case 'v': |
| 209 | 212 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| ... | ... |
@@ -220,6 +223,9 @@ int main(int argc, char *argv[]){
|
| 220 | 223 |
case 't': |
| 221 | 224 |
tty_filename = optarg; |
| 222 | 225 |
break; |
| 226 |
+ case 'p': |
|
| 227 |
+ prompt = optarg; |
|
| 228 |
+ break; |
|
| 223 | 229 |
case 'l': |
| 224 | 230 |
{
|
| 225 | 231 |
int l; |
| ... | ... |
@@ -152,6 +152,7 @@ void run(tty_t *tty, choices_t *choices){
|
| 152 | 152 |
}else if(ch == 9){ /* TAB */
|
| 153 | 153 |
strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); |
| 154 | 154 |
search_size = strlen(search); |
| 155 |
+ choices_search(choices, search); |
|
| 155 | 156 |
}else if(ch == 10){ /* Enter */
|
| 156 | 157 |
clear(tty); |
| 157 | 158 |
|
| ... | ... |
@@ -4,6 +4,7 @@ |
| 4 | 4 |
#include <stdlib.h> |
| 5 | 5 |
#include <ctype.h> |
| 6 | 6 |
#include <getopt.h> |
| 7 |
+#include <limits.h> |
|
| 7 | 8 |
|
| 8 | 9 |
#include "match.h" |
| 9 | 10 |
#include "tty.h" |
| ... | ... |
@@ -222,7 +223,9 @@ int main(int argc, char *argv[]){
|
| 222 | 223 |
case 'l': |
| 223 | 224 |
{
|
| 224 | 225 |
int l; |
| 225 |
- if(sscanf(optarg, "%d", &l) != 1 || l < 3){
|
|
| 226 |
+ if(!strcmp(optarg, "max")){
|
|
| 227 |
+ l = INT_MAX; |
|
| 228 |
+ }else if(sscanf(optarg, "%d", &l) != 1 || l < 3){
|
|
| 226 | 229 |
fprintf(stderr, "Invalid format for --lines: %s\n", optarg); |
| 227 | 230 |
fprintf(stderr, "Must be integer in range 3..\n"); |
| 228 | 231 |
usage(argv[0]); |
| ... | ... |
@@ -246,6 +246,9 @@ int main(int argc, char *argv[]){
|
| 246 | 246 |
choices_init(&choices); |
| 247 | 247 |
read_choices(&choices); |
| 248 | 248 |
|
| 249 |
+ if(num_lines > choices.size) |
|
| 250 |
+ num_lines = choices.size; |
|
| 251 |
+ |
|
| 249 | 252 |
if(benchmark){
|
| 250 | 253 |
if(!initial_query){
|
| 251 | 254 |
fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
| ... | ... |
@@ -265,6 +268,9 @@ int main(int argc, char *argv[]){
|
| 265 | 268 |
tty_t tty; |
| 266 | 269 |
tty_init(&tty, tty_filename); |
| 267 | 270 |
|
| 271 |
+ if(num_lines + 1 > tty_getheight(&tty)) |
|
| 272 |
+ num_lines = tty_getheight(&tty) - 1; |
|
| 273 |
+ |
|
| 268 | 274 |
run(&tty, &choices); |
| 269 | 275 |
} |
| 270 | 276 |
|
| ... | ... |
@@ -9,6 +9,8 @@ |
| 9 | 9 |
#include "tty.h" |
| 10 | 10 |
#include "choices.h" |
| 11 | 11 |
|
| 12 |
+#include "config.h" |
|
| 13 |
+ |
|
| 12 | 14 |
int flag_show_scores = 0; |
| 13 | 15 |
|
| 14 | 16 |
size_t num_lines = 10; |
| ... | ... |
@@ -45,8 +47,6 @@ void clear(tty_t *tty){
|
| 45 | 47 |
tty_flush(tty); |
| 46 | 48 |
} |
| 47 | 49 |
|
| 48 |
-#define TTY_COLOR_HIGHLIGHT TTY_COLOR_YELLOW |
|
| 49 |
- |
|
| 50 | 50 |
void draw_match(tty_t *tty, const char *choice, int selected){
|
| 51 | 51 |
int n = strlen(search); |
| 52 | 52 |
size_t positions[n + 1]; |
| ... | ... |
@@ -55,6 +55,8 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 55 | 55 |
|
| 56 | 56 |
double score = match_positions(search, choice, &positions[0]); |
| 57 | 57 |
|
| 58 |
+ size_t maxwidth = tty_getwidth(tty); |
|
| 59 |
+ |
|
| 58 | 60 |
if(flag_show_scores) |
| 59 | 61 |
tty_printf(tty, "(%5.2f) ", score); |
| 60 | 62 |
|
| ... | ... |
@@ -62,13 +64,18 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 62 | 64 |
tty_setinvert(tty); |
| 63 | 65 |
|
| 64 | 66 |
for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
| 65 |
- if(positions[p] == i){
|
|
| 66 |
- tty_setfg(tty, TTY_COLOR_HIGHLIGHT); |
|
| 67 |
- p++; |
|
| 67 |
+ if(i+1 < maxwidth){
|
|
| 68 |
+ if(positions[p] == i){
|
|
| 69 |
+ tty_setfg(tty, TTY_COLOR_HIGHLIGHT); |
|
| 70 |
+ p++; |
|
| 71 |
+ }else{
|
|
| 72 |
+ tty_setfg(tty, TTY_COLOR_NORMAL); |
|
| 73 |
+ } |
|
| 74 |
+ tty_printf(tty, "%c", choice[i]); |
|
| 68 | 75 |
}else{
|
| 69 |
- tty_setfg(tty, TTY_COLOR_NORMAL); |
|
| 76 |
+ tty_printf(tty, "$"); |
|
| 77 |
+ break; |
|
| 70 | 78 |
} |
| 71 |
- tty_printf(tty, "%c", choice[i]); |
|
| 72 | 79 |
} |
| 73 | 80 |
tty_setnormal(tty); |
| 74 | 81 |
} |
| ... | ... |
@@ -85,14 +92,15 @@ void draw(tty_t *tty, choices_t *choices){
|
| 85 | 92 |
const char *prompt = "> "; |
| 86 | 93 |
tty_setcol(tty, 0); |
| 87 | 94 |
tty_printf(tty, "%s%s", prompt, search); |
| 95 |
+ tty_clearline(tty); |
|
| 88 | 96 |
for(size_t i = start; i < start + num_lines; i++){
|
| 89 |
- tty_newline(tty); |
|
| 97 |
+ tty_printf(tty, "\n"); |
|
| 98 |
+ tty_clearline(tty); |
|
| 90 | 99 |
const char *choice = choices_get(choices, i); |
| 91 | 100 |
if(choice){
|
| 92 | 101 |
draw_match(tty, choice, i == choices->selection); |
| 93 | 102 |
} |
| 94 | 103 |
} |
| 95 |
- tty_clearline(tty); |
|
| 96 | 104 |
tty_moveup(tty, num_lines); |
| 97 | 105 |
tty_setcol(tty, strlen(prompt) + strlen(search)); |
| 98 | 106 |
tty_flush(tty); |
| ... | ... |
@@ -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; |
| ... | ... |
@@ -111,7 +111,10 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 111 | 111 |
for(int i = 0; i < n + 1; i++) |
| 112 | 112 |
positions[i] = -1; |
| 113 | 113 |
|
| 114 |
- match_positions(search, choice, &positions[0]); |
|
| 114 |
+ double score = match_positions(search, choice, &positions[0]); |
|
| 115 |
+ |
|
| 116 |
+ if(flag_show_scores) |
|
| 117 |
+ tty_printf(tty, "(%5.2f) ", score); |
|
| 115 | 118 |
|
| 116 | 119 |
if(selected) |
| 117 | 120 |
tty_setinvert(tty); |
| ... | ... |
@@ -143,8 +146,6 @@ void draw(tty_t *tty){
|
| 143 | 146 |
tty_newline(tty); |
| 144 | 147 |
if(i < choices_available){
|
| 145 | 148 |
size_t choice_idx = choices_sorted[i]; |
| 146 |
- if(flag_show_scores) |
|
| 147 |
- tty_printf(tty, "(%5.2f) ", choices_score[choice_idx]); |
|
| 148 | 149 |
draw_match(tty, choices[choice_idx], i == current_selection); |
| 149 | 150 |
} |
| 150 | 151 |
} |
| ... | ... |
@@ -207,6 +207,9 @@ void run(tty_t *tty){
|
| 207 | 207 |
action_next(); |
| 208 | 208 |
}else if(ch == 16){ /* C-P */
|
| 209 | 209 |
action_prev(); |
| 210 |
+ }else if(ch == 9){ /* TAB */
|
|
| 211 |
+ strncpy(search, choices[choices_sorted[current_selection]], SEARCH_SIZE_MAX); |
|
| 212 |
+ search_size = strlen(search); |
|
| 210 | 213 |
}else if(ch == 10){ /* Enter */
|
| 211 | 214 |
clear(tty); |
| 212 | 215 |
emit(tty); |
| ... | ... |
@@ -225,9 +225,10 @@ void run(tty_t *tty){
|
| 225 | 225 |
} |
| 226 | 226 |
|
| 227 | 227 |
static const char *usage_str = "" |
| 228 |
-"USAGE: fzy [OPTION]...\n" |
|
| 229 |
-" -l, --lines=LINES Specify how many lines of results to show\n" |
|
| 228 |
+"Usage: fzy [OPTION]...\n" |
|
| 229 |
+" -l, --lines=LINES Specify how many lines of results to show (default 10)\n" |
|
| 230 | 230 |
" -e, --show-matches=QUERY output the sorted matches of QUERY\n" |
| 231 |
+" -t, --tty=TTY Specify file to use as TTY device (default /dev/tty)\n" |
|
| 231 | 232 |
" -s, --show-scores show the scores of each match\n" |
| 232 | 233 |
" -h, --help display this help and exit\n" |
| 233 | 234 |
" -v, --version output version information and exit\n"; |
| ... | ... |
@@ -239,6 +240,7 @@ void usage(const char *argv0){
|
| 239 | 240 |
static struct option longopts[] = {
|
| 240 | 241 |
{ "show-matches", required_argument, NULL, 'e' },
|
| 241 | 242 |
{ "lines", required_argument, NULL, 'l' },
|
| 243 |
+ { "tty", required_argument, NULL, 't' },
|
|
| 242 | 244 |
{ "show-scores", no_argument, NULL, 's' },
|
| 243 | 245 |
{ "version", no_argument, NULL, 'v' },
|
| 244 | 246 |
{ "benchmark", no_argument, NULL, 'b' },
|
| ... | ... |
@@ -249,8 +251,9 @@ static struct option longopts[] = {
|
| 249 | 251 |
int main(int argc, char *argv[]){
|
| 250 | 252 |
int benchmark = 0; |
| 251 | 253 |
char *initial_query = NULL; |
| 254 |
+ char *tty_filename = "/dev/tty"; |
|
| 252 | 255 |
char c; |
| 253 |
- while((c = getopt_long(argc, argv, "vhse:l:", longopts, NULL)) != -1){
|
|
| 256 |
+ while((c = getopt_long(argc, argv, "vhse:l:t:", longopts, NULL)) != -1){
|
|
| 254 | 257 |
switch(c){
|
| 255 | 258 |
case 'v': |
| 256 | 259 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| ... | ... |
@@ -264,6 +267,9 @@ int main(int argc, char *argv[]){
|
| 264 | 267 |
case 'b': |
| 265 | 268 |
benchmark = 1; |
| 266 | 269 |
break; |
| 270 |
+ case 't': |
|
| 271 |
+ tty_filename = optarg; |
|
| 272 |
+ break; |
|
| 267 | 273 |
case 'l': |
| 268 | 274 |
{
|
| 269 | 275 |
int l; |
| ... | ... |
@@ -308,7 +314,7 @@ int main(int argc, char *argv[]){
|
| 308 | 314 |
}else{
|
| 309 | 315 |
/* interactive */ |
| 310 | 316 |
tty_t tty; |
| 311 |
- tty_init(&tty); |
|
| 317 |
+ tty_init(&tty, tty_filename); |
|
| 312 | 318 |
|
| 313 | 319 |
run(&tty); |
| 314 | 320 |
} |
| ... | ... |
@@ -226,7 +226,7 @@ void run(tty_t *tty){
|
| 226 | 226 |
|
| 227 | 227 |
static const char *usage_str = "" |
| 228 | 228 |
"USAGE: fzy [OPTION]...\n" |
| 229 |
-" -l, --lines Specify how many lines of results to show\n" |
|
| 229 |
+" -l, --lines=LINES Specify how many lines of results to show\n" |
|
| 230 | 230 |
" -e, --show-matches=QUERY output the sorted matches of QUERY\n" |
| 231 | 231 |
" -s, --show-scores show the scores of each match\n" |
| 232 | 232 |
" -h, --help display this help and exit\n" |
| ... | ... |
@@ -241,11 +241,13 @@ static struct option longopts[] = {
|
| 241 | 241 |
{ "lines", required_argument, NULL, 'l' },
|
| 242 | 242 |
{ "show-scores", no_argument, NULL, 's' },
|
| 243 | 243 |
{ "version", no_argument, NULL, 'v' },
|
| 244 |
+ { "benchmark", no_argument, NULL, 'b' },
|
|
| 244 | 245 |
{ "help", no_argument, NULL, 'h' },
|
| 245 | 246 |
{ NULL, 0, NULL, 0 }
|
| 246 | 247 |
}; |
| 247 | 248 |
|
| 248 | 249 |
int main(int argc, char *argv[]){
|
| 250 |
+ int benchmark = 0; |
|
| 249 | 251 |
char *initial_query = NULL; |
| 250 | 252 |
char c; |
| 251 | 253 |
while((c = getopt_long(argc, argv, "vhse:l:", longopts, NULL)) != -1){
|
| ... | ... |
@@ -259,6 +261,9 @@ int main(int argc, char *argv[]){
|
| 259 | 261 |
case 'e': |
| 260 | 262 |
initial_query = optarg; |
| 261 | 263 |
break; |
| 264 |
+ case 'b': |
|
| 265 |
+ benchmark = 1; |
|
| 266 |
+ break; |
|
| 262 | 267 |
case 'l': |
| 263 | 268 |
{
|
| 264 | 269 |
int l; |
| ... | ... |
@@ -285,7 +290,14 @@ int main(int argc, char *argv[]){
|
| 285 | 290 |
resize_choices(INITIAL_CAPACITY); |
| 286 | 291 |
read_choices(); |
| 287 | 292 |
|
| 288 |
- if(initial_query){
|
|
| 293 |
+ if(benchmark){
|
|
| 294 |
+ if(!initial_query){
|
|
| 295 |
+ fprintf(stderr, "Must specify -e/--show-matches with --benchmark\n"); |
|
| 296 |
+ exit(EXIT_FAILURE); |
|
| 297 |
+ } |
|
| 298 |
+ for(int i = 0; i < 100; i++) |
|
| 299 |
+ run_search(initial_query); |
|
| 300 |
+ }else if(initial_query){
|
|
| 289 | 301 |
run_search(initial_query); |
| 290 | 302 |
for(size_t i = 0; i < choices_available; i++){
|
| 291 | 303 |
size_t choice_idx = choices_sorted[i]; |
| ... | ... |
@@ -10,6 +10,10 @@ |
| 10 | 10 |
|
| 11 | 11 |
int flag_show_scores = 0; |
| 12 | 12 |
|
| 13 |
+size_t num_lines = 10; |
|
| 14 |
+size_t scrolloff = 1; |
|
| 15 |
+ |
|
| 16 |
+ |
|
| 13 | 17 |
#define INITIAL_CAPACITY 1 |
| 14 | 18 |
int choices_capacity = 0; |
| 15 | 19 |
int choices_n = 0; |
| ... | ... |
@@ -85,17 +89,14 @@ void run_search(char *needle){
|
| 85 | 89 |
qsort(choices_sorted, choices_available, sizeof(size_t), cmpchoice); |
| 86 | 90 |
} |
| 87 | 91 |
|
| 88 |
-#define NUMLINES 10 |
|
| 89 |
-#define SCROLLOFF 1 |
|
| 90 |
- |
|
| 91 | 92 |
#define SEARCH_SIZE_MAX 4096 |
| 92 | 93 |
int search_size; |
| 93 | 94 |
char search[SEARCH_SIZE_MAX + 1] = {0};
|
| 94 | 95 |
|
| 95 | 96 |
void clear(tty_t *tty){
|
| 96 | 97 |
tty_setcol(tty, 0); |
| 97 |
- int line = 0; |
|
| 98 |
- while(line++ < NUMLINES){
|
|
| 98 |
+ size_t line = 0; |
|
| 99 |
+ while(line++ < num_lines){
|
|
| 99 | 100 |
tty_newline(tty); |
| 100 | 101 |
} |
| 101 | 102 |
tty_moveup(tty, line-1); |
| ... | ... |
@@ -129,16 +130,16 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 129 | 130 |
|
| 130 | 131 |
void draw(tty_t *tty){
|
| 131 | 132 |
size_t start = 0; |
| 132 |
- if(current_selection + SCROLLOFF >= NUMLINES){
|
|
| 133 |
- start = current_selection + SCROLLOFF - NUMLINES + 1; |
|
| 134 |
- if(start + NUMLINES >= choices_available){
|
|
| 135 |
- start = choices_available - NUMLINES; |
|
| 133 |
+ if(current_selection + scrolloff >= num_lines){
|
|
| 134 |
+ start = current_selection + scrolloff - num_lines + 1; |
|
| 135 |
+ if(start + num_lines >= choices_available){
|
|
| 136 |
+ start = choices_available - num_lines; |
|
| 136 | 137 |
} |
| 137 | 138 |
} |
| 138 | 139 |
const char *prompt = "> "; |
| 139 | 140 |
tty_setcol(tty, 0); |
| 140 | 141 |
tty_printf(tty, "%s%s", prompt, search); |
| 141 |
- for(size_t i = start; i < start + NUMLINES; i++){
|
|
| 142 |
+ for(size_t i = start; i < start + num_lines; i++){
|
|
| 142 | 143 |
tty_newline(tty); |
| 143 | 144 |
if(i < choices_available){
|
| 144 | 145 |
size_t choice_idx = choices_sorted[i]; |
| ... | ... |
@@ -148,7 +149,7 @@ void draw(tty_t *tty){
|
| 148 | 149 |
} |
| 149 | 150 |
} |
| 150 | 151 |
tty_clearline(tty); |
| 151 |
- tty_moveup(tty, NUMLINES); |
|
| 152 |
+ tty_moveup(tty, num_lines); |
|
| 152 | 153 |
tty_setcol(tty, strlen(prompt) + strlen(search)); |
| 153 | 154 |
tty_flush(tty); |
| 154 | 155 |
} |
| ... | ... |
@@ -225,6 +226,7 @@ void run(tty_t *tty){
|
| 225 | 226 |
|
| 226 | 227 |
static const char *usage_str = "" |
| 227 | 228 |
"USAGE: fzy [OPTION]...\n" |
| 229 |
+" -l, --lines Specify how many lines of results to show\n" |
|
| 228 | 230 |
" -e, --show-matches=QUERY output the sorted matches of QUERY\n" |
| 229 | 231 |
" -s, --show-scores show the scores of each match\n" |
| 230 | 232 |
" -h, --help display this help and exit\n" |
| ... | ... |
@@ -232,11 +234,11 @@ static const char *usage_str = "" |
| 232 | 234 |
|
| 233 | 235 |
void usage(const char *argv0){
|
| 234 | 236 |
fprintf(stderr, usage_str, argv0); |
| 235 |
- exit(EXIT_FAILURE); |
|
| 236 | 237 |
} |
| 237 | 238 |
|
| 238 | 239 |
static struct option longopts[] = {
|
| 239 | 240 |
{ "show-matches", required_argument, NULL, 'e' },
|
| 241 |
+ { "lines", required_argument, NULL, 'l' },
|
|
| 240 | 242 |
{ "show-scores", no_argument, NULL, 's' },
|
| 241 | 243 |
{ "version", no_argument, NULL, 'v' },
|
| 242 | 244 |
{ "help", no_argument, NULL, 'h' },
|
| ... | ... |
@@ -246,7 +248,7 @@ static struct option longopts[] = {
|
| 246 | 248 |
int main(int argc, char *argv[]){
|
| 247 | 249 |
char *initial_query = NULL; |
| 248 | 250 |
char c; |
| 249 |
- while((c = getopt_long(argc, argv, "vhse:", longopts, NULL)) != -1){
|
|
| 251 |
+ while((c = getopt_long(argc, argv, "vhse:l:", longopts, NULL)) != -1){
|
|
| 250 | 252 |
switch(c){
|
| 251 | 253 |
case 'v': |
| 252 | 254 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| ... | ... |
@@ -257,14 +259,27 @@ int main(int argc, char *argv[]){
|
| 257 | 259 |
case 'e': |
| 258 | 260 |
initial_query = optarg; |
| 259 | 261 |
break; |
| 262 |
+ case 'l': |
|
| 263 |
+ {
|
|
| 264 |
+ int l; |
|
| 265 |
+ if(sscanf(optarg, "%d", &l) != 1 || l < 3){
|
|
| 266 |
+ fprintf(stderr, "Invalid format for --lines: %s\n", optarg); |
|
| 267 |
+ fprintf(stderr, "Must be integer in range 3..\n"); |
|
| 268 |
+ usage(argv[0]); |
|
| 269 |
+ exit(EXIT_FAILURE); |
|
| 270 |
+ } |
|
| 271 |
+ num_lines = l; |
|
| 272 |
+ } |
|
| 273 |
+ break; |
|
| 260 | 274 |
case 'h': |
| 261 | 275 |
default: |
| 262 | 276 |
usage(argv[0]); |
| 263 |
- exit(EXIT_FAILURE); |
|
| 277 |
+ exit(EXIT_SUCCESS); |
|
| 264 | 278 |
} |
| 265 | 279 |
} |
| 266 | 280 |
if(optind != argc){
|
| 267 | 281 |
usage(argv[0]); |
| 282 |
+ exit(EXIT_FAILURE); |
|
| 268 | 283 |
} |
| 269 | 284 |
|
| 270 | 285 |
resize_choices(INITIAL_CAPACITY); |
| ... | ... |
@@ -95,7 +95,7 @@ char search[SEARCH_SIZE_MAX + 1] = {0};
|
| 95 | 95 |
void clear(tty_t *tty){
|
| 96 | 96 |
tty_setcol(tty, 0); |
| 97 | 97 |
int line = 0; |
| 98 |
- while(line++ < NUMLINES + 1){
|
|
| 98 |
+ while(line++ < NUMLINES){
|
|
| 99 | 99 |
tty_newline(tty); |
| 100 | 100 |
} |
| 101 | 101 |
tty_moveup(tty, line-1); |
| ... | ... |
@@ -132,24 +132,23 @@ void draw(tty_t *tty){
|
| 132 | 132 |
if(current_selection + SCROLLOFF >= NUMLINES){
|
| 133 | 133 |
start = current_selection + SCROLLOFF - NUMLINES + 1; |
| 134 | 134 |
if(start + NUMLINES >= choices_available){
|
| 135 |
- start = choices_available - NUMLINES + 1; |
|
| 135 |
+ start = choices_available - NUMLINES; |
|
| 136 | 136 |
} |
| 137 | 137 |
} |
| 138 | 138 |
const char *prompt = "> "; |
| 139 | 139 |
tty_setcol(tty, 0); |
| 140 | 140 |
tty_printf(tty, "%s%s", prompt, search); |
| 141 |
- tty_newline(tty); |
|
| 142 | 141 |
for(size_t i = start; i < start + NUMLINES; i++){
|
| 142 |
+ tty_newline(tty); |
|
| 143 | 143 |
if(i < choices_available){
|
| 144 | 144 |
size_t choice_idx = choices_sorted[i]; |
| 145 | 145 |
if(flag_show_scores) |
| 146 | 146 |
tty_printf(tty, "(%5.2f) ", choices_score[choice_idx]); |
| 147 | 147 |
draw_match(tty, choices[choice_idx], i == current_selection); |
| 148 |
- }else{
|
|
| 149 |
- tty_newline(tty); |
|
| 150 | 148 |
} |
| 151 | 149 |
} |
| 152 |
- tty_moveup(tty, NUMLINES + 1); |
|
| 150 |
+ tty_clearline(tty); |
|
| 151 |
+ tty_moveup(tty, NUMLINES); |
|
| 153 | 152 |
tty_setcol(tty, strlen(prompt) + strlen(search)); |
| 154 | 153 |
tty_flush(tty); |
| 155 | 154 |
} |
| ... | ... |
@@ -86,6 +86,7 @@ void run_search(char *needle){
|
| 86 | 86 |
} |
| 87 | 87 |
|
| 88 | 88 |
#define NUMLINES 10 |
| 89 |
+#define SCROLLOFF 1 |
|
| 89 | 90 |
|
| 90 | 91 |
#define SEARCH_SIZE_MAX 4096 |
| 91 | 92 |
int search_size; |
| ... | ... |
@@ -123,14 +124,16 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 123 | 124 |
} |
| 124 | 125 |
tty_printf(tty, "%c", choice[i]); |
| 125 | 126 |
} |
| 126 |
- tty_newline(tty); |
|
| 127 | 127 |
tty_setnormal(tty); |
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 | 130 |
void draw(tty_t *tty){
|
| 131 | 131 |
size_t start = 0; |
| 132 |
- if(current_selection >= NUMLINES){
|
|
| 133 |
- start = current_selection - NUMLINES + 1; |
|
| 132 |
+ if(current_selection + SCROLLOFF >= NUMLINES){
|
|
| 133 |
+ start = current_selection + SCROLLOFF - NUMLINES + 1; |
|
| 134 |
+ if(start + NUMLINES >= choices_available){
|
|
| 135 |
+ start = choices_available - NUMLINES + 1; |
|
| 136 |
+ } |
|
| 134 | 137 |
} |
| 135 | 138 |
const char *prompt = "> "; |
| 136 | 139 |
tty_setcol(tty, 0); |
| ... | ... |
@@ -244,7 +244,7 @@ static struct option longopts[] = {
|
| 244 | 244 |
int main(int argc, char *argv[]){
|
| 245 | 245 |
char *initial_query = NULL; |
| 246 | 246 |
char c; |
| 247 |
- while((c = getopt_long(argc, argv, "vhs", longopts, NULL)) != -1){
|
|
| 247 |
+ while((c = getopt_long(argc, argv, "vhse:", longopts, NULL)) != -1){
|
|
| 248 | 248 |
switch(c){
|
| 249 | 249 |
case 'v': |
| 250 | 250 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| ... | ... |
@@ -223,6 +223,7 @@ void run(tty_t *tty){
|
| 223 | 223 |
|
| 224 | 224 |
static const char *usage_str = "" |
| 225 | 225 |
"USAGE: fzy [OPTION]...\n" |
| 226 |
+" -e, --show-matches=QUERY output the sorted matches of QUERY\n" |
|
| 226 | 227 |
" -s, --show-scores show the scores of each match\n" |
| 227 | 228 |
" -h, --help display this help and exit\n" |
| 228 | 229 |
" -v, --version output version information and exit\n"; |
| ... | ... |
@@ -233,14 +234,15 @@ void usage(const char *argv0){
|
| 233 | 234 |
} |
| 234 | 235 |
|
| 235 | 236 |
static struct option longopts[] = {
|
| 237 |
+ { "show-matches", required_argument, NULL, 'e' },
|
|
| 236 | 238 |
{ "show-scores", no_argument, NULL, 's' },
|
| 237 | 239 |
{ "version", no_argument, NULL, 'v' },
|
| 238 | 240 |
{ "help", no_argument, NULL, 'h' },
|
| 239 | 241 |
{ NULL, 0, NULL, 0 }
|
| 240 | 242 |
}; |
| 241 | 243 |
|
| 242 |
- |
|
| 243 | 244 |
int main(int argc, char *argv[]){
|
| 245 |
+ char *initial_query = NULL; |
|
| 244 | 246 |
char c; |
| 245 | 247 |
while((c = getopt_long(argc, argv, "vhs", longopts, NULL)) != -1){
|
| 246 | 248 |
switch(c){
|
| ... | ... |
@@ -250,6 +252,9 @@ int main(int argc, char *argv[]){
|
| 250 | 252 |
case 's': |
| 251 | 253 |
flag_show_scores = 1; |
| 252 | 254 |
break; |
| 255 |
+ case 'e': |
|
| 256 |
+ initial_query = optarg; |
|
| 257 |
+ break; |
|
| 253 | 258 |
case 'h': |
| 254 | 259 |
default: |
| 255 | 260 |
usage(argv[0]); |
| ... | ... |
@@ -260,14 +265,25 @@ int main(int argc, char *argv[]){
|
| 260 | 265 |
usage(argv[0]); |
| 261 | 266 |
} |
| 262 | 267 |
|
| 263 |
- tty_t tty; |
|
| 264 |
- tty_init(&tty); |
|
| 265 |
- |
|
| 266 | 268 |
resize_choices(INITIAL_CAPACITY); |
| 267 | 269 |
read_choices(); |
| 268 | 270 |
|
| 269 |
- clear(&tty); |
|
| 270 |
- run(&tty); |
|
| 271 |
+ if(initial_query){
|
|
| 272 |
+ run_search(initial_query); |
|
| 273 |
+ for(size_t i = 0; i < choices_available; i++){
|
|
| 274 |
+ size_t choice_idx = choices_sorted[i]; |
|
| 275 |
+ if(flag_show_scores) |
|
| 276 |
+ printf("%f\t", choices_score[choice_idx]);
|
|
| 277 |
+ printf("%s\n", choices[choice_idx]);
|
|
| 278 |
+ } |
|
| 279 |
+ }else{
|
|
| 280 |
+ /* interactive */ |
|
| 281 |
+ tty_t tty; |
|
| 282 |
+ tty_init(&tty); |
|
| 283 |
+ |
|
| 284 |
+ clear(&tty); |
|
| 285 |
+ run(&tty); |
|
| 286 |
+ } |
|
| 271 | 287 |
|
| 272 | 288 |
return 0; |
| 273 | 289 |
} |
| ... | ... |
@@ -8,6 +8,8 @@ |
| 8 | 8 |
#include "fzy.h" |
| 9 | 9 |
#include "tty.h" |
| 10 | 10 |
|
| 11 |
+int flag_show_scores = 0; |
|
| 12 |
+ |
|
| 11 | 13 |
#define INITIAL_CAPACITY 1 |
| 12 | 14 |
int choices_capacity = 0; |
| 13 | 15 |
int choices_n = 0; |
| ... | ... |
@@ -136,7 +138,10 @@ void draw(tty_t *tty){
|
| 136 | 138 |
tty_newline(tty); |
| 137 | 139 |
for(size_t i = start; i < start + NUMLINES; i++){
|
| 138 | 140 |
if(i < choices_available){
|
| 139 |
- draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
|
| 141 |
+ size_t choice_idx = choices_sorted[i]; |
|
| 142 |
+ if(flag_show_scores) |
|
| 143 |
+ tty_printf(tty, "(%5.2f) ", choices_score[choice_idx]); |
|
| 144 |
+ draw_match(tty, choices[choice_idx], i == current_selection); |
|
| 140 | 145 |
}else{
|
| 141 | 146 |
tty_newline(tty); |
| 142 | 147 |
} |
| ... | ... |
@@ -218,6 +223,7 @@ void run(tty_t *tty){
|
| 218 | 223 |
|
| 219 | 224 |
static const char *usage_str = "" |
| 220 | 225 |
"USAGE: fzy [OPTION]...\n" |
| 226 |
+" -s, --show-scores show the scores of each match\n" |
|
| 221 | 227 |
" -h, --help display this help and exit\n" |
| 222 | 228 |
" -v, --version output version information and exit\n"; |
| 223 | 229 |
|
| ... | ... |
@@ -227,6 +233,7 @@ void usage(const char *argv0){
|
| 227 | 233 |
} |
| 228 | 234 |
|
| 229 | 235 |
static struct option longopts[] = {
|
| 236 |
+ { "show-scores", no_argument, NULL, 's' },
|
|
| 230 | 237 |
{ "version", no_argument, NULL, 'v' },
|
| 231 | 238 |
{ "help", no_argument, NULL, 'h' },
|
| 232 | 239 |
{ NULL, 0, NULL, 0 }
|
| ... | ... |
@@ -235,11 +242,14 @@ static struct option longopts[] = {
|
| 235 | 242 |
|
| 236 | 243 |
int main(int argc, char *argv[]){
|
| 237 | 244 |
char c; |
| 238 |
- while((c = getopt_long(argc, argv, "vh", longopts, NULL)) != -1){
|
|
| 245 |
+ while((c = getopt_long(argc, argv, "vhs", longopts, NULL)) != -1){
|
|
| 239 | 246 |
switch(c){
|
| 240 | 247 |
case 'v': |
| 241 | 248 |
printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
| 242 | 249 |
exit(EXIT_SUCCESS); |
| 250 |
+ case 's': |
|
| 251 |
+ flag_show_scores = 1; |
|
| 252 |
+ break; |
|
| 243 | 253 |
case 'h': |
| 244 | 254 |
default: |
| 245 | 255 |
usage(argv[0]); |
| ... | ... |
@@ -3,6 +3,7 @@ |
| 3 | 3 |
#include <string.h> |
| 4 | 4 |
#include <stdlib.h> |
| 5 | 5 |
#include <ctype.h> |
| 6 |
+#include <getopt.h> |
|
| 6 | 7 |
|
| 7 | 8 |
#include "fzy.h" |
| 8 | 9 |
#include "tty.h" |
| ... | ... |
@@ -215,18 +216,40 @@ void run(tty_t *tty){
|
| 215 | 216 |
}while(1); |
| 216 | 217 |
} |
| 217 | 218 |
|
| 219 |
+static const char *usage_str = "" |
|
| 220 |
+"USAGE: fzy [OPTION]...\n" |
|
| 221 |
+" -h, --help display this help and exit\n" |
|
| 222 |
+" -v, --version output version information and exit\n"; |
|
| 223 |
+ |
|
| 218 | 224 |
void usage(const char *argv0){
|
| 219 |
- fprintf(stderr, "USAGE: %s\n", argv0); |
|
| 225 |
+ fprintf(stderr, usage_str, argv0); |
|
| 220 | 226 |
exit(EXIT_FAILURE); |
| 221 | 227 |
} |
| 222 | 228 |
|
| 229 |
+static struct option longopts[] = {
|
|
| 230 |
+ { "version", no_argument, NULL, 'v' },
|
|
| 231 |
+ { "help", no_argument, NULL, 'h' },
|
|
| 232 |
+ { NULL, 0, NULL, 0 }
|
|
| 233 |
+}; |
|
| 234 |
+ |
|
| 235 |
+ |
|
| 223 | 236 |
int main(int argc, char *argv[]){
|
| 224 |
- if(argc == 2 && !strcmp(argv[1], "-v")){
|
|
| 225 |
- printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
|
| 226 |
- exit(EXIT_SUCCESS); |
|
| 227 |
- }else if(argc != 1){
|
|
| 237 |
+ char c; |
|
| 238 |
+ while((c = getopt_long(argc, argv, "vh", longopts, NULL)) != -1){
|
|
| 239 |
+ switch(c){
|
|
| 240 |
+ case 'v': |
|
| 241 |
+ printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
|
| 242 |
+ exit(EXIT_SUCCESS); |
|
| 243 |
+ case 'h': |
|
| 244 |
+ default: |
|
| 245 |
+ usage(argv[0]); |
|
| 246 |
+ exit(EXIT_FAILURE); |
|
| 247 |
+ } |
|
| 248 |
+ } |
|
| 249 |
+ if(optind != argc){
|
|
| 228 | 250 |
usage(argv[0]); |
| 229 | 251 |
} |
| 252 |
+ |
|
| 230 | 253 |
tty_t tty; |
| 231 | 254 |
tty_init(&tty); |
| 232 | 255 |
|
| ... | ... |
@@ -125,24 +125,22 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
void draw(tty_t *tty){
|
| 128 |
- int start = 0; |
|
| 128 |
+ size_t start = 0; |
|
| 129 | 129 |
if(current_selection >= NUMLINES){
|
| 130 | 130 |
start = current_selection - NUMLINES + 1; |
| 131 | 131 |
} |
| 132 |
- int line = 0; |
|
| 133 | 132 |
const char *prompt = "> "; |
| 134 | 133 |
tty_setcol(tty, 0); |
| 135 | 134 |
tty_printf(tty, "%s%s", prompt, search); |
| 136 | 135 |
tty_newline(tty); |
| 137 |
- for(size_t i = start; line < NUMLINES; i++){
|
|
| 136 |
+ for(size_t i = start; i < start + NUMLINES; i++){
|
|
| 138 | 137 |
if(i < choices_available){
|
| 139 | 138 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| 140 | 139 |
}else{
|
| 141 | 140 |
tty_newline(tty); |
| 142 | 141 |
} |
| 143 |
- line++; |
|
| 144 | 142 |
} |
| 145 |
- tty_moveup(tty, line + 1); |
|
| 143 |
+ tty_moveup(tty, NUMLINES + 1); |
|
| 146 | 144 |
tty_setcol(tty, strlen(prompt) + strlen(search)); |
| 147 | 145 |
tty_flush(tty); |
| 148 | 146 |
} |
Instead of clearing the existing text before redrawing, clear as we draw
using tty_newline (CSI-K). This looks smoother when scrolling under
xterm and probably other terminals.
| ... | ... |
@@ -120,7 +120,7 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 120 | 120 |
} |
| 121 | 121 |
tty_printf(tty, "%c", choice[i]); |
| 122 | 122 |
} |
| 123 |
- tty_printf(tty, "\n"); |
|
| 123 |
+ tty_newline(tty); |
|
| 124 | 124 |
tty_setnormal(tty); |
| 125 | 125 |
} |
| 126 | 126 |
|
| ... | ... |
@@ -131,10 +131,15 @@ void draw(tty_t *tty){
|
| 131 | 131 |
} |
| 132 | 132 |
int line = 0; |
| 133 | 133 |
const char *prompt = "> "; |
| 134 |
- clear(tty); |
|
| 135 |
- tty_printf(tty, "%s%s\n", prompt, search); |
|
| 136 |
- for(size_t i = start; line < NUMLINES && i < choices_available; i++){
|
|
| 137 |
- draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
|
| 134 |
+ tty_setcol(tty, 0); |
|
| 135 |
+ tty_printf(tty, "%s%s", prompt, search); |
|
| 136 |
+ tty_newline(tty); |
|
| 137 |
+ for(size_t i = start; line < NUMLINES; i++){
|
|
| 138 |
+ if(i < choices_available){
|
|
| 139 |
+ draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
|
| 140 |
+ }else{
|
|
| 141 |
+ tty_newline(tty); |
|
| 142 |
+ } |
|
| 138 | 143 |
line++; |
| 139 | 144 |
} |
| 140 | 145 |
tty_moveup(tty, line + 1); |
| ... | ... |
@@ -94,7 +94,7 @@ void clear(tty_t *tty){
|
| 94 | 94 |
while(line++ < NUMLINES + 1){
|
| 95 | 95 |
tty_newline(tty); |
| 96 | 96 |
} |
| 97 |
- fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line-1); |
|
| 97 |
+ tty_moveup(tty, line-1); |
|
| 98 | 98 |
tty_setcol(tty, 0); |
| 99 | 99 |
} |
| 100 | 100 |
|
| ... | ... |
@@ -137,7 +137,7 @@ void draw(tty_t *tty){
|
| 137 | 137 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| 138 | 138 |
line++; |
| 139 | 139 |
} |
| 140 |
- fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line + 1); |
|
| 140 |
+ tty_moveup(tty, line + 1); |
|
| 141 | 141 |
tty_setcol(tty, strlen(prompt) + strlen(search) + 1); |
| 142 | 142 |
fflush(tty->fout); |
| 143 | 143 |
} |
| ... | ... |
@@ -89,13 +89,13 @@ int search_size; |
| 89 | 89 |
char search[SEARCH_SIZE_MAX + 1] = {0};
|
| 90 | 90 |
|
| 91 | 91 |
void clear(tty_t *tty){
|
| 92 |
- fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
|
| 92 |
+ tty_setcol(tty, 0); |
|
| 93 | 93 |
int line = 0; |
| 94 | 94 |
while(line++ < NUMLINES + 1){
|
| 95 |
- fprintf(tty->fout, "%c%cK\n", 0x1b, '['); |
|
| 95 |
+ tty_newline(tty); |
|
| 96 | 96 |
} |
| 97 | 97 |
fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line-1); |
| 98 |
- fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
|
| 98 |
+ tty_setcol(tty, 0); |
|
| 99 | 99 |
} |
| 100 | 100 |
|
| 101 | 101 |
#define TTY_COLOR_HIGHLIGHT TTY_COLOR_YELLOW |
| ... | ... |
@@ -138,7 +138,7 @@ void draw(tty_t *tty){
|
| 138 | 138 |
line++; |
| 139 | 139 |
} |
| 140 | 140 |
fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line + 1); |
| 141 |
- fprintf(tty->fout, "%c%c%ziG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 141 |
+ tty_setcol(tty, strlen(prompt) + strlen(search) + 1); |
|
| 142 | 142 |
fflush(tty->fout); |
| 143 | 143 |
} |
| 144 | 144 |
|
| ... | ... |
@@ -118,9 +118,9 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 118 | 118 |
}else{
|
| 119 | 119 |
tty_setfg(tty, TTY_COLOR_NORMAL); |
| 120 | 120 |
} |
| 121 |
- fprintf(tty->fout, "%c", choice[i]); |
|
| 121 |
+ tty_printf(tty, "%c", choice[i]); |
|
| 122 | 122 |
} |
| 123 |
- fprintf(tty->fout, "\n"); |
|
| 123 |
+ tty_printf(tty, "\n"); |
|
| 124 | 124 |
tty_setnormal(tty); |
| 125 | 125 |
} |
| 126 | 126 |
|
| ... | ... |
@@ -132,7 +132,7 @@ void draw(tty_t *tty){
|
| 132 | 132 |
int line = 0; |
| 133 | 133 |
const char *prompt = "> "; |
| 134 | 134 |
clear(tty); |
| 135 |
- fprintf(tty->fout, "%s%s\n", prompt, search); |
|
| 135 |
+ tty_printf(tty, "%s%s\n", prompt, search); |
|
| 136 | 136 |
for(size_t i = start; line < NUMLINES && i < choices_available; i++){
|
| 137 | 137 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| 138 | 138 |
line++; |
| ... | ... |
@@ -198,6 +198,16 @@ void run(tty_t *tty){
|
| 198 | 198 |
}else if(ch == 10){ /* Enter */
|
| 199 | 199 |
clear(tty); |
| 200 | 200 |
emit(tty); |
| 201 |
+ }else if(ch == 27){ /* ESC */
|
|
| 202 |
+ ch = tty_getchar(tty); |
|
| 203 |
+ if(ch == '['){
|
|
| 204 |
+ ch = tty_getchar(tty); |
|
| 205 |
+ if(ch == 'A'){ /* UP ARROW */
|
|
| 206 |
+ action_prev(); |
|
| 207 |
+ }else if(ch == 'B'){ /* DOWN ARROW */
|
|
| 208 |
+ action_next(); |
|
| 209 |
+ } |
|
| 210 |
+ } |
|
| 201 | 211 |
} |
| 202 | 212 |
}while(1); |
| 203 | 213 |
} |
| ... | ... |
@@ -125,11 +125,15 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
void draw(tty_t *tty){
|
| 128 |
+ int start = 0; |
|
| 129 |
+ if(current_selection >= NUMLINES){
|
|
| 130 |
+ start = current_selection - NUMLINES + 1; |
|
| 131 |
+ } |
|
| 128 | 132 |
int line = 0; |
| 129 | 133 |
const char *prompt = "> "; |
| 130 | 134 |
clear(tty); |
| 131 | 135 |
fprintf(tty->fout, "%s%s\n", prompt, search); |
| 132 |
- for(size_t i = 0; line < NUMLINES && i < choices_available; i++){
|
|
| 136 |
+ for(size_t i = start; line < NUMLINES && i < choices_available; i++){
|
|
| 133 | 137 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| 134 | 138 |
line++; |
| 135 | 139 |
} |
| ... | ... |
@@ -154,11 +158,11 @@ void emit(tty_t *tty){
|
| 154 | 158 |
} |
| 155 | 159 |
|
| 156 | 160 |
void action_prev(){
|
| 157 |
- current_selection = (current_selection + NUMLINES - 1) % NUMLINES; |
|
| 161 |
+ current_selection = (current_selection + choices_available - 1) % choices_available; |
|
| 158 | 162 |
} |
| 159 | 163 |
|
| 160 | 164 |
void action_next(){
|
| 161 |
- current_selection = (current_selection + 1) % NUMLINES; |
|
| 165 |
+ current_selection = (current_selection + 1) % choices_available; |
|
| 162 | 166 |
} |
| 163 | 167 |
|
| 164 | 168 |
void run(tty_t *tty){
|
| ... | ... |
@@ -153,6 +153,14 @@ void emit(tty_t *tty){
|
| 153 | 153 |
exit(EXIT_SUCCESS); |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
+void action_prev(){
|
|
| 157 |
+ current_selection = (current_selection + NUMLINES - 1) % NUMLINES; |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+void action_next(){
|
|
| 161 |
+ current_selection = (current_selection + 1) % NUMLINES; |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 156 | 164 |
void run(tty_t *tty){
|
| 157 | 165 |
run_search(search); |
| 158 | 166 |
char ch; |
| ... | ... |
@@ -180,9 +188,9 @@ void run(tty_t *tty){
|
| 180 | 188 |
search[search_size] = '\0'; |
| 181 | 189 |
run_search(search); |
| 182 | 190 |
}else if(ch == 14){ /* C-N */
|
| 183 |
- current_selection = (current_selection + 1) % NUMLINES; |
|
| 191 |
+ action_next(); |
|
| 184 | 192 |
}else if(ch == 16){ /* C-P */
|
| 185 |
- current_selection = (current_selection + NUMLINES - 1) % NUMLINES; |
|
| 193 |
+ action_prev(); |
|
| 186 | 194 |
}else if(ch == 10){ /* Enter */
|
| 187 | 195 |
clear(tty); |
| 188 | 196 |
emit(tty); |
| ... | ... |
@@ -98,6 +98,8 @@ void clear(tty_t *tty){
|
| 98 | 98 |
fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
| 99 | 99 |
} |
| 100 | 100 |
|
| 101 |
+#define TTY_COLOR_HIGHLIGHT TTY_COLOR_YELLOW |
|
| 102 |
+ |
|
| 101 | 103 |
void draw_match(tty_t *tty, const char *choice, int selected){
|
| 102 | 104 |
int n = strlen(search); |
| 103 | 105 |
size_t positions[n + 1]; |
| ... | ... |
@@ -111,10 +113,10 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 111 | 113 |
|
| 112 | 114 |
for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
| 113 | 115 |
if(positions[p] == i){
|
| 114 |
- tty_setfg(tty, 3); |
|
| 116 |
+ tty_setfg(tty, TTY_COLOR_HIGHLIGHT); |
|
| 115 | 117 |
p++; |
| 116 | 118 |
}else{
|
| 117 |
- tty_setfg(tty, 9); |
|
| 119 |
+ tty_setfg(tty, TTY_COLOR_NORMAL); |
|
| 118 | 120 |
} |
| 119 | 121 |
fprintf(tty->fout, "%c", choice[i]); |
| 120 | 122 |
} |
| ... | ... |
@@ -106,17 +106,20 @@ void draw_match(tty_t *tty, const char *choice, int selected){
|
| 106 | 106 |
|
| 107 | 107 |
match_positions(search, choice, &positions[0]); |
| 108 | 108 |
|
| 109 |
+ if(selected) |
|
| 110 |
+ tty_setinvert(tty); |
|
| 111 |
+ |
|
| 109 | 112 |
for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
| 110 | 113 |
if(positions[p] == i){
|
| 111 |
- fprintf(tty->fout, "%c%c33m", 0x1b, '['); |
|
| 114 |
+ tty_setfg(tty, 3); |
|
| 112 | 115 |
p++; |
| 113 | 116 |
}else{
|
| 114 |
- fprintf(tty->fout, "%c%c39;49m", 0x1b, '['); |
|
| 117 |
+ tty_setfg(tty, 9); |
|
| 115 | 118 |
} |
| 116 | 119 |
fprintf(tty->fout, "%c", choice[i]); |
| 117 | 120 |
} |
| 118 | 121 |
fprintf(tty->fout, "\n"); |
| 119 |
- fprintf(tty->fout, "%c%c0m", 0x1b, '['); |
|
| 122 |
+ tty_setnormal(tty); |
|
| 120 | 123 |
} |
| 121 | 124 |
|
| 122 | 125 |
void draw(tty_t *tty){
|
| ... | ... |
@@ -125,8 +128,6 @@ void draw(tty_t *tty){
|
| 125 | 128 |
clear(tty); |
| 126 | 129 |
fprintf(tty->fout, "%s%s\n", prompt, search); |
| 127 | 130 |
for(size_t i = 0; line < NUMLINES && i < choices_available; i++){
|
| 128 |
- if(i == current_selection) |
|
| 129 |
- fprintf(tty->fout, "%c%c7m", 0x1b, '['); |
|
| 130 | 131 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| 131 | 132 |
line++; |
| 132 | 133 |
} |
| ... | ... |
@@ -84,8 +84,9 @@ void run_search(char *needle){
|
| 84 | 84 |
|
| 85 | 85 |
#define NUMLINES 10 |
| 86 | 86 |
|
| 87 |
+#define SEARCH_SIZE_MAX 4096 |
|
| 87 | 88 |
int search_size; |
| 88 |
-char search[4096] = {0};
|
|
| 89 |
+char search[SEARCH_SIZE_MAX + 1] = {0};
|
|
| 89 | 90 |
|
| 90 | 91 |
void clear(tty_t *tty){
|
| 91 | 92 |
fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
| ... | ... |
@@ -156,10 +157,11 @@ void run(tty_t *tty){
|
| 156 | 157 |
draw(tty); |
| 157 | 158 |
ch = tty_getchar(tty); |
| 158 | 159 |
if(isprint(ch)){
|
| 159 |
- /* FIXME: overflow */ |
|
| 160 |
- search[search_size++] = ch; |
|
| 161 |
- search[search_size] = '\0'; |
|
| 162 |
- run_search(search); |
|
| 160 |
+ if(search_size < SEARCH_SIZE_MAX){
|
|
| 161 |
+ search[search_size++] = ch; |
|
| 162 |
+ search[search_size] = '\0'; |
|
| 163 |
+ run_search(search); |
|
| 164 |
+ } |
|
| 163 | 165 |
}else if(ch == 127 || ch == 8){ /* DEL || backspace */
|
| 164 | 166 |
if(search_size) |
| 165 | 167 |
search[--search_size] = '\0'; |
| ... | ... |
@@ -82,13 +82,15 @@ void run_search(char *needle){
|
| 82 | 82 |
qsort(choices_sorted, choices_available, sizeof(size_t), cmpchoice); |
| 83 | 83 |
} |
| 84 | 84 |
|
| 85 |
+#define NUMLINES 10 |
|
| 86 |
+ |
|
| 85 | 87 |
int search_size; |
| 86 | 88 |
char search[4096] = {0};
|
| 87 | 89 |
|
| 88 | 90 |
void clear(tty_t *tty){
|
| 89 | 91 |
fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
| 90 | 92 |
int line = 0; |
| 91 |
- while(line++ < 10 + 1){
|
|
| 93 |
+ while(line++ < NUMLINES + 1){
|
|
| 92 | 94 |
fprintf(tty->fout, "%c%cK\n", 0x1b, '['); |
| 93 | 95 |
} |
| 94 | 96 |
fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line-1); |
| ... | ... |
@@ -121,7 +123,7 @@ void draw(tty_t *tty){
|
| 121 | 123 |
const char *prompt = "> "; |
| 122 | 124 |
clear(tty); |
| 123 | 125 |
fprintf(tty->fout, "%s%s\n", prompt, search); |
| 124 |
- for(size_t i = 0; line < 10 && i < choices_available; i++){
|
|
| 126 |
+ for(size_t i = 0; line < NUMLINES && i < choices_available; i++){
|
|
| 125 | 127 |
if(i == current_selection) |
| 126 | 128 |
fprintf(tty->fout, "%c%c7m", 0x1b, '['); |
| 127 | 129 |
draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
| ... | ... |
@@ -173,9 +175,9 @@ void run(tty_t *tty){
|
| 173 | 175 |
search[search_size] = '\0'; |
| 174 | 176 |
run_search(search); |
| 175 | 177 |
}else if(ch == 14){ /* C-N */
|
| 176 |
- current_selection = (current_selection + 1) % 10; |
|
| 178 |
+ current_selection = (current_selection + 1) % NUMLINES; |
|
| 177 | 179 |
}else if(ch == 16){ /* C-P */
|
| 178 |
- current_selection = (current_selection + 9) % 10; |
|
| 180 |
+ current_selection = (current_selection + NUMLINES - 1) % NUMLINES; |
|
| 179 | 181 |
}else if(ch == 10){ /* Enter */
|
| 180 | 182 |
clear(tty); |
| 181 | 183 |
emit(tty); |
| ... | ... |
@@ -2,13 +2,10 @@ |
| 2 | 2 |
#include <stdio.h> |
| 3 | 3 |
#include <string.h> |
| 4 | 4 |
#include <stdlib.h> |
| 5 |
-#include <termios.h> |
|
| 6 |
-#include <unistd.h> |
|
| 7 |
-#include <sys/stat.h> |
|
| 8 |
-#include <fcntl.h> |
|
| 9 | 5 |
#include <ctype.h> |
| 10 | 6 |
|
| 11 | 7 |
#include "fzy.h" |
| 8 |
+#include "tty.h" |
|
| 12 | 9 |
|
| 13 | 10 |
#define INITIAL_CAPACITY 1 |
| 14 | 11 |
int choices_capacity = 0; |
| ... | ... |
@@ -83,58 +80,22 @@ void run_search(char *needle){
|
| 83 | 80 |
} |
| 84 | 81 |
|
| 85 | 82 |
qsort(choices_sorted, choices_available, sizeof(size_t), cmpchoice); |
| 86 |
- |
|
| 87 |
-} |
|
| 88 |
- |
|
| 89 |
-int ttyin; |
|
| 90 |
-FILE *ttyout; |
|
| 91 |
-struct termios original_termios; |
|
| 92 |
- |
|
| 93 |
-void reset_tty(){
|
|
| 94 |
- tcsetattr(ttyin, TCSANOW, &original_termios); |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-void init_tty(){
|
|
| 98 |
- ttyin = open("/dev/tty", O_RDONLY);
|
|
| 99 |
- ttyout = fopen("/dev/tty", "w");
|
|
| 100 |
- |
|
| 101 |
- tcgetattr(ttyin, &original_termios); |
|
| 102 |
- |
|
| 103 |
- struct termios new_termios = original_termios; |
|
| 104 |
- |
|
| 105 |
- new_termios.c_lflag &= ~(ICANON | ECHO); |
|
| 106 |
- |
|
| 107 |
- tcsetattr(ttyin, TCSANOW, &new_termios); |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-char ttygetchar(){
|
|
| 111 |
- char ch; |
|
| 112 |
- int size = read(ttyin, &ch, 1); |
|
| 113 |
- if(size < 0){
|
|
| 114 |
- perror("error reading from tty");
|
|
| 115 |
- exit(EXIT_FAILURE); |
|
| 116 |
- }else if(size == 0){
|
|
| 117 |
- /* EOF */ |
|
| 118 |
- exit(EXIT_FAILURE); |
|
| 119 |
- }else{
|
|
| 120 |
- return ch; |
|
| 121 |
- } |
|
| 122 | 83 |
} |
| 123 | 84 |
|
| 124 | 85 |
int search_size; |
| 125 | 86 |
char search[4096] = {0};
|
| 126 | 87 |
|
| 127 |
-void clear(){
|
|
| 128 |
- fprintf(ttyout, "%c%c0G", 0x1b, '['); |
|
| 88 |
+void clear(tty_t *tty){
|
|
| 89 |
+ fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
|
| 129 | 90 |
int line = 0; |
| 130 | 91 |
while(line++ < 10 + 1){
|
| 131 |
- fprintf(ttyout, "%c%cK\n", 0x1b, '['); |
|
| 92 |
+ fprintf(tty->fout, "%c%cK\n", 0x1b, '['); |
|
| 132 | 93 |
} |
| 133 |
- fprintf(ttyout, "%c%c%iA", 0x1b, '[', line-1); |
|
| 134 |
- fprintf(ttyout, "%c%c0G", 0x1b, '['); |
|
| 94 |
+ fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line-1); |
|
| 95 |
+ fprintf(tty->fout, "%c%c0G", 0x1b, '['); |
|
| 135 | 96 |
} |
| 136 | 97 |
|
| 137 |
-void draw_match(const char *choice, int selected){
|
|
| 98 |
+void draw_match(tty_t *tty, const char *choice, int selected){
|
|
| 138 | 99 |
int n = strlen(search); |
| 139 | 100 |
size_t positions[n + 1]; |
| 140 | 101 |
for(int i = 0; i < n + 1; i++) |
| ... | ... |
@@ -144,36 +105,36 @@ void draw_match(const char *choice, int selected){
|
| 144 | 105 |
|
| 145 | 106 |
for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
| 146 | 107 |
if(positions[p] == i){
|
| 147 |
- fprintf(ttyout, "%c%c33m", 0x1b, '['); |
|
| 108 |
+ fprintf(tty->fout, "%c%c33m", 0x1b, '['); |
|
| 148 | 109 |
p++; |
| 149 | 110 |
}else{
|
| 150 |
- fprintf(ttyout, "%c%c39;49m", 0x1b, '['); |
|
| 111 |
+ fprintf(tty->fout, "%c%c39;49m", 0x1b, '['); |
|
| 151 | 112 |
} |
| 152 |
- fprintf(ttyout, "%c", choice[i]); |
|
| 113 |
+ fprintf(tty->fout, "%c", choice[i]); |
|
| 153 | 114 |
} |
| 154 |
- fprintf(ttyout, "\n"); |
|
| 155 |
- fprintf(ttyout, "%c%c0m", 0x1b, '['); |
|
| 115 |
+ fprintf(tty->fout, "\n"); |
|
| 116 |
+ fprintf(tty->fout, "%c%c0m", 0x1b, '['); |
|
| 156 | 117 |
} |
| 157 | 118 |
|
| 158 |
-void draw(){
|
|
| 119 |
+void draw(tty_t *tty){
|
|
| 159 | 120 |
int line = 0; |
| 160 | 121 |
const char *prompt = "> "; |
| 161 |
- clear(); |
|
| 162 |
- fprintf(ttyout, "%s%s\n", prompt, search); |
|
| 122 |
+ clear(tty); |
|
| 123 |
+ fprintf(tty->fout, "%s%s\n", prompt, search); |
|
| 163 | 124 |
for(size_t i = 0; line < 10 && i < choices_available; i++){
|
| 164 | 125 |
if(i == current_selection) |
| 165 |
- fprintf(ttyout, "%c%c7m", 0x1b, '['); |
|
| 166 |
- draw_match(choices[choices_sorted[i]], i == current_selection); |
|
| 126 |
+ fprintf(tty->fout, "%c%c7m", 0x1b, '['); |
|
| 127 |
+ draw_match(tty, choices[choices_sorted[i]], i == current_selection); |
|
| 167 | 128 |
line++; |
| 168 | 129 |
} |
| 169 |
- fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1); |
|
| 170 |
- fprintf(ttyout, "%c%c%ziG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 171 |
- fflush(ttyout); |
|
| 130 |
+ fprintf(tty->fout, "%c%c%iA", 0x1b, '[', line + 1); |
|
| 131 |
+ fprintf(tty->fout, "%c%c%ziG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 132 |
+ fflush(tty->fout); |
|
| 172 | 133 |
} |
| 173 | 134 |
|
| 174 |
-void emit(){
|
|
| 135 |
+void emit(tty_t *tty){
|
|
| 175 | 136 |
/* ttyout should be flushed before outputting on stdout */ |
| 176 |
- fclose(ttyout); |
|
| 137 |
+ fclose(tty->fout); |
|
| 177 | 138 |
|
| 178 | 139 |
if(choices_available){
|
| 179 | 140 |
/* output the selected result */ |
| ... | ... |
@@ -186,12 +147,12 @@ void emit(){
|
| 186 | 147 |
exit(EXIT_SUCCESS); |
| 187 | 148 |
} |
| 188 | 149 |
|
| 189 |
-void run(){
|
|
| 150 |
+void run(tty_t *tty){
|
|
| 190 | 151 |
run_search(search); |
| 191 | 152 |
char ch; |
| 192 | 153 |
do {
|
| 193 |
- draw(); |
|
| 194 |
- ch = ttygetchar(); |
|
| 154 |
+ draw(tty); |
|
| 155 |
+ ch = tty_getchar(tty); |
|
| 195 | 156 |
if(isprint(ch)){
|
| 196 | 157 |
/* FIXME: overflow */ |
| 197 | 158 |
search[search_size++] = ch; |
| ... | ... |
@@ -213,13 +174,11 @@ void run(){
|
| 213 | 174 |
run_search(search); |
| 214 | 175 |
}else if(ch == 14){ /* C-N */
|
| 215 | 176 |
current_selection = (current_selection + 1) % 10; |
| 216 |
- draw(); |
|
| 217 | 177 |
}else if(ch == 16){ /* C-P */
|
| 218 | 178 |
current_selection = (current_selection + 9) % 10; |
| 219 |
- draw(); |
|
| 220 | 179 |
}else if(ch == 10){ /* Enter */
|
| 221 |
- clear(); |
|
| 222 |
- emit(); |
|
| 180 |
+ clear(tty); |
|
| 181 |
+ emit(tty); |
|
| 223 | 182 |
} |
| 224 | 183 |
}while(1); |
| 225 | 184 |
} |
| ... | ... |
@@ -236,14 +195,14 @@ int main(int argc, char *argv[]){
|
| 236 | 195 |
}else if(argc != 1){
|
| 237 | 196 |
usage(argv[0]); |
| 238 | 197 |
} |
| 239 |
- atexit(reset_tty); |
|
| 240 |
- init_tty(reset_tty); |
|
| 198 |
+ tty_t tty; |
|
| 199 |
+ tty_init(&tty); |
|
| 241 | 200 |
|
| 242 | 201 |
resize_choices(INITIAL_CAPACITY); |
| 243 | 202 |
read_choices(); |
| 244 | 203 |
|
| 245 |
- clear(); |
|
| 246 |
- run(); |
|
| 204 |
+ clear(&tty); |
|
| 205 |
+ run(&tty); |
|
| 247 | 206 |
|
| 248 | 207 |
return 0; |
| 249 | 208 |
} |
| ... | ... |
@@ -64,8 +64,7 @@ static int cmpchoice(const void *p1, const void *p2) {
|
| 64 | 64 |
double score2 = choices_score[idx2]; |
| 65 | 65 |
|
| 66 | 66 |
if(score1 == score2) |
| 67 |
- /* break ties by length of result */ |
|
| 68 |
- return strlen(choices[idx1]) - strlen(choices[idx2]); |
|
| 67 |
+ return 0; |
|
| 69 | 68 |
else if(score1 < score2) |
| 70 | 69 |
return 1; |
| 71 | 70 |
else |
This reverts commit e77435e4c26cc5d2c879452d9310890763df0b96.
| ... | ... |
@@ -63,7 +63,13 @@ static int cmpchoice(const void *p1, const void *p2) {
|
| 63 | 63 |
double score1 = choices_score[idx1]; |
| 64 | 64 |
double score2 = choices_score[idx2]; |
| 65 | 65 |
|
| 66 |
- return score1 - score2; |
|
| 66 |
+ if(score1 == score2) |
|
| 67 |
+ /* break ties by length of result */ |
|
| 68 |
+ return strlen(choices[idx1]) - strlen(choices[idx2]); |
|
| 69 |
+ else if(score1 < score2) |
|
| 70 |
+ return 1; |
|
| 71 |
+ else |
|
| 72 |
+ return -1; |
|
| 67 | 73 |
} |
| 68 | 74 |
|
| 69 | 75 |
void run_search(char *needle){
|
| ... | ... |
@@ -63,13 +63,7 @@ static int cmpchoice(const void *p1, const void *p2) {
|
| 63 | 63 |
double score1 = choices_score[idx1]; |
| 64 | 64 |
double score2 = choices_score[idx2]; |
| 65 | 65 |
|
| 66 |
- if(score1 == score2) |
|
| 67 |
- /* break ties by length of result */ |
|
| 68 |
- return strlen(choices[idx1]) - strlen(choices[idx2]); |
|
| 69 |
- else if(score1 < score2) |
|
| 70 |
- return 1; |
|
| 71 |
- else |
|
| 72 |
- return -1; |
|
| 66 |
+ return score1 - score2; |
|
| 73 | 67 |
} |
| 74 | 68 |
|
| 75 | 69 |
void run_search(char *needle){
|
| ... | ... |
@@ -232,7 +232,10 @@ void usage(const char *argv0){
|
| 232 | 232 |
} |
| 233 | 233 |
|
| 234 | 234 |
int main(int argc, char *argv[]){
|
| 235 |
- if(argc != 1){
|
|
| 235 |
+ if(argc == 2 && !strcmp(argv[1], "-v")){
|
|
| 236 |
+ printf("%s " VERSION " (c) 2014 John Hawthorn\n", argv[0]);
|
|
| 237 |
+ exit(EXIT_SUCCESS); |
|
| 238 |
+ }else if(argc != 1){
|
|
| 236 | 239 |
usage(argv[0]); |
| 237 | 240 |
} |
| 238 | 241 |
atexit(reset_tty); |
Previously a successful match was determined by the score being
positive. Now we will use has_match instead.
| ... | ... |
@@ -77,8 +77,8 @@ void run_search(char *needle){
|
| 77 | 77 |
choices_available = 0; |
| 78 | 78 |
int i; |
| 79 | 79 |
for(i = 0; i < choices_n; i++){
|
| 80 |
- choices_score[i] = match(needle, choices[i]); |
|
| 81 |
- if(choices_score[i] >= 0.0){
|
|
| 80 |
+ if(has_match(needle, choices[i])){
|
|
| 81 |
+ choices_score[i] = match(needle, choices[i]); |
|
| 82 | 82 |
choices_sorted[choices_available++] = i; |
| 83 | 83 |
} |
| 84 | 84 |
} |
| ... | ... |
@@ -8,9 +8,7 @@ |
| 8 | 8 |
#include <fcntl.h> |
| 9 | 9 |
#include <ctype.h> |
| 10 | 10 |
|
| 11 |
-/* from match.c */ |
|
| 12 |
-double match(const char *needle, const char *haystack); |
|
| 13 |
-double match_positions(const char *needle, const char *haystack, size_t *positions); |
|
| 11 |
+#include "fzy.h" |
|
| 14 | 12 |
|
| 15 | 13 |
#define INITIAL_CAPACITY 1 |
| 16 | 14 |
int choices_capacity = 0; |
| ... | ... |
@@ -10,6 +10,7 @@ |
| 10 | 10 |
|
| 11 | 11 |
/* from match.c */ |
| 12 | 12 |
double match(const char *needle, const char *haystack); |
| 13 |
+double match_positions(const char *needle, const char *haystack, size_t *positions); |
|
| 13 | 14 |
|
| 14 | 15 |
#define INITIAL_CAPACITY 1 |
| 15 | 16 |
int choices_capacity = 0; |
| ... | ... |
@@ -136,6 +137,27 @@ void clear(){
|
| 136 | 137 |
fprintf(ttyout, "%c%c0G", 0x1b, '['); |
| 137 | 138 |
} |
| 138 | 139 |
|
| 140 |
+void draw_match(const char *choice, int selected){
|
|
| 141 |
+ int n = strlen(search); |
|
| 142 |
+ size_t positions[n + 1]; |
|
| 143 |
+ for(int i = 0; i < n + 1; i++) |
|
| 144 |
+ positions[i] = -1; |
|
| 145 |
+ |
|
| 146 |
+ match_positions(search, choice, &positions[0]); |
|
| 147 |
+ |
|
| 148 |
+ for(size_t i = 0, p = 0; choice[i] != '\0'; i++){
|
|
| 149 |
+ if(positions[p] == i){
|
|
| 150 |
+ fprintf(ttyout, "%c%c33m", 0x1b, '['); |
|
| 151 |
+ p++; |
|
| 152 |
+ }else{
|
|
| 153 |
+ fprintf(ttyout, "%c%c39;49m", 0x1b, '['); |
|
| 154 |
+ } |
|
| 155 |
+ fprintf(ttyout, "%c", choice[i]); |
|
| 156 |
+ } |
|
| 157 |
+ fprintf(ttyout, "\n"); |
|
| 158 |
+ fprintf(ttyout, "%c%c0m", 0x1b, '['); |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 139 | 161 |
void draw(){
|
| 140 | 162 |
int line = 0; |
| 141 | 163 |
const char *prompt = "> "; |
| ... | ... |
@@ -144,9 +166,7 @@ void draw(){
|
| 144 | 166 |
for(size_t i = 0; line < 10 && i < choices_available; i++){
|
| 145 | 167 |
if(i == current_selection) |
| 146 | 168 |
fprintf(ttyout, "%c%c7m", 0x1b, '['); |
| 147 |
- else |
|
| 148 |
- fprintf(ttyout, "%c%c0m", 0x1b, '['); |
|
| 149 |
- fprintf(ttyout, "%s\n", choices[choices_sorted[i]]); |
|
| 169 |
+ draw_match(choices[choices_sorted[i]], i == current_selection); |
|
| 150 | 170 |
line++; |
| 151 | 171 |
} |
| 152 | 172 |
fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1); |
Change-Id: Ib14ab12e6223267f397b506939f0e6c6f5d3c468
| ... | ... |
@@ -180,10 +180,13 @@ void run(){
|
| 180 | 180 |
search[search_size++] = ch; |
| 181 | 181 |
search[search_size] = '\0'; |
| 182 | 182 |
run_search(search); |
| 183 |
- }else if(ch == 127){ /* DEL */
|
|
| 183 |
+ }else if(ch == 127 || ch == 8){ /* DEL || backspace */
|
|
| 184 | 184 |
if(search_size) |
| 185 | 185 |
search[--search_size] = '\0'; |
| 186 | 186 |
run_search(search); |
| 187 |
+ }else if(ch == 21){ /* C-U */
|
|
| 188 |
+ search_size = 0; |
|
| 189 |
+ search[0] = '\0'; |
|
| 187 | 190 |
}else if(ch == 23){ /* C-W */
|
| 188 | 191 |
if(search_size) |
| 189 | 192 |
search[--search_size] = '\0'; |
| ... | ... |
@@ -1,3 +1,4 @@ |
| 1 |
+#define _GNU_SOURCE |
|
| 1 | 2 |
#include <stdio.h> |
| 2 | 3 |
#include <string.h> |
| 3 | 4 |
#include <stdlib.h> |
| ... | ... |
@@ -16,7 +17,7 @@ int choices_n = 0; |
| 16 | 17 |
const char **choices = NULL; |
| 17 | 18 |
double *choices_score = NULL; |
| 18 | 19 |
size_t *choices_sorted = NULL; |
| 19 |
-int current_selection = 0; |
|
| 20 |
+size_t current_selection = 0; |
|
| 20 | 21 |
|
| 21 | 22 |
void resize_choices(int new_capacity){
|
| 22 | 23 |
choices = realloc(choices, new_capacity * sizeof(const char *)); |
| ... | ... |
@@ -137,11 +138,10 @@ void clear(){
|
| 137 | 138 |
|
| 138 | 139 |
void draw(){
|
| 139 | 140 |
int line = 0; |
| 140 |
- int i; |
|
| 141 | 141 |
const char *prompt = "> "; |
| 142 | 142 |
clear(); |
| 143 | 143 |
fprintf(ttyout, "%s%s\n", prompt, search); |
| 144 |
- for(i = 0; line < 10 && i < choices_available; i++){
|
|
| 144 |
+ for(size_t i = 0; line < 10 && i < choices_available; i++){
|
|
| 145 | 145 |
if(i == current_selection) |
| 146 | 146 |
fprintf(ttyout, "%c%c7m", 0x1b, '['); |
| 147 | 147 |
else |
| ... | ... |
@@ -150,7 +150,7 @@ void draw(){
|
| 150 | 150 |
line++; |
| 151 | 151 |
} |
| 152 | 152 |
fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1); |
| 153 |
- fprintf(ttyout, "%c%c%iG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 153 |
+ fprintf(ttyout, "%c%c%ziG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 154 | 154 |
fflush(ttyout); |
| 155 | 155 |
} |
| 156 | 156 |
|
| ... | ... |
@@ -64,7 +64,8 @@ static int cmpchoice(const void *p1, const void *p2) {
|
| 64 | 64 |
double score2 = choices_score[idx2]; |
| 65 | 65 |
|
| 66 | 66 |
if(score1 == score2) |
| 67 |
- return 0; |
|
| 67 |
+ /* break ties by length of result */ |
|
| 68 |
+ return strlen(choices[idx1]) - strlen(choices[idx2]); |
|
| 68 | 69 |
else if(score1 < score2) |
| 69 | 70 |
return 1; |
| 70 | 71 |
else |
| ... | ... |
@@ -16,6 +16,7 @@ int choices_n = 0; |
| 16 | 16 |
const char **choices = NULL; |
| 17 | 17 |
double *choices_score = NULL; |
| 18 | 18 |
size_t *choices_sorted = NULL; |
| 19 |
+int current_selection = 0; |
|
| 19 | 20 |
|
| 20 | 21 |
void resize_choices(int new_capacity){
|
| 21 | 22 |
choices = realloc(choices, new_capacity * sizeof(const char *)); |
| ... | ... |
@@ -70,8 +71,8 @@ static int cmpchoice(const void *p1, const void *p2) {
|
| 70 | 71 |
return -1; |
| 71 | 72 |
} |
| 72 | 73 |
|
| 73 |
- |
|
| 74 | 74 |
void run_search(char *needle){
|
| 75 |
+ current_selection = 0; |
|
| 75 | 76 |
choices_available = 0; |
| 76 | 77 |
int i; |
| 77 | 78 |
for(i = 0; i < choices_n; i++){
|
| ... | ... |
@@ -139,8 +140,11 @@ void draw(){
|
| 139 | 140 |
const char *prompt = "> "; |
| 140 | 141 |
clear(); |
| 141 | 142 |
fprintf(ttyout, "%s%s\n", prompt, search); |
| 142 |
- run_search(search); |
|
| 143 | 143 |
for(i = 0; line < 10 && i < choices_available; i++){
|
| 144 |
+ if(i == current_selection) |
|
| 145 |
+ fprintf(ttyout, "%c%c7m", 0x1b, '['); |
|
| 146 |
+ else |
|
| 147 |
+ fprintf(ttyout, "%c%c0m", 0x1b, '['); |
|
| 144 | 148 |
fprintf(ttyout, "%s\n", choices[choices_sorted[i]]); |
| 145 | 149 |
line++; |
| 146 | 150 |
} |
| ... | ... |
@@ -153,10 +157,9 @@ void emit(){
|
| 153 | 157 |
/* ttyout should be flushed before outputting on stdout */ |
| 154 | 158 |
fclose(ttyout); |
| 155 | 159 |
|
| 156 |
- run_search(search); |
|
| 157 | 160 |
if(choices_available){
|
| 158 |
- /* output the first result */ |
|
| 159 |
- printf("%s\n", choices[choices_sorted[0]]);
|
|
| 161 |
+ /* output the selected result */ |
|
| 162 |
+ printf("%s\n", choices[choices_sorted[current_selection]]);
|
|
| 160 | 163 |
}else{
|
| 161 | 164 |
/* No match, output the query instead */ |
| 162 | 165 |
printf("%s\n", search);
|
| ... | ... |
@@ -166,24 +169,31 @@ void emit(){
|
| 166 | 169 |
} |
| 167 | 170 |
|
| 168 | 171 |
void run(){
|
| 169 |
- draw(); |
|
| 172 |
+ run_search(search); |
|
| 170 | 173 |
char ch; |
| 171 | 174 |
do {
|
| 175 |
+ draw(); |
|
| 172 | 176 |
ch = ttygetchar(); |
| 173 | 177 |
if(isprint(ch)){
|
| 174 | 178 |
/* FIXME: overflow */ |
| 175 | 179 |
search[search_size++] = ch; |
| 176 | 180 |
search[search_size] = '\0'; |
| 177 |
- draw(); |
|
| 181 |
+ run_search(search); |
|
| 178 | 182 |
}else if(ch == 127){ /* DEL */
|
| 179 | 183 |
if(search_size) |
| 180 | 184 |
search[--search_size] = '\0'; |
| 181 |
- draw(); |
|
| 185 |
+ run_search(search); |
|
| 182 | 186 |
}else if(ch == 23){ /* C-W */
|
| 183 | 187 |
if(search_size) |
| 184 | 188 |
search[--search_size] = '\0'; |
| 185 | 189 |
while(search_size && !isspace(search[--search_size])) |
| 186 | 190 |
search[search_size] = '\0'; |
| 191 |
+ run_search(search); |
|
| 192 |
+ }else if(ch == 14){ /* C-N */
|
|
| 193 |
+ current_selection = (current_selection + 1) % 10; |
|
| 194 |
+ draw(); |
|
| 195 |
+ }else if(ch == 16){ /* C-P */
|
|
| 196 |
+ current_selection = (current_selection + 9) % 10; |
|
| 187 | 197 |
draw(); |
| 188 | 198 |
}else if(ch == 10){ /* Enter */
|
| 189 | 199 |
clear(); |
| ... | ... |
@@ -14,9 +14,14 @@ double match(const char *needle, const char *haystack); |
| 14 | 14 |
int choices_capacity = 0; |
| 15 | 15 |
int choices_n = 0; |
| 16 | 16 |
const char **choices = NULL; |
| 17 |
+double *choices_score = NULL; |
|
| 18 |
+size_t *choices_sorted = NULL; |
|
| 17 | 19 |
|
| 18 | 20 |
void resize_choices(int new_capacity){
|
| 19 | 21 |
choices = realloc(choices, new_capacity * sizeof(const char *)); |
| 22 |
+ choices_score = realloc(choices_score, new_capacity * sizeof(double)); |
|
| 23 |
+ choices_sorted = realloc(choices_sorted, new_capacity * sizeof(size_t)); |
|
| 24 |
+ |
|
| 20 | 25 |
int i = choices_capacity; |
| 21 | 26 |
for(; i < new_capacity; i++){
|
| 22 | 27 |
choices[i] = NULL; |
| ... | ... |
@@ -48,14 +53,36 @@ void read_choices(){
|
| 48 | 53 |
free(line); |
| 49 | 54 |
} |
| 50 | 55 |
|
| 56 |
+size_t choices_available = 0; |
|
| 57 |
+ |
|
| 58 |
+static int cmpchoice(const void *p1, const void *p2) {
|
|
| 59 |
+ size_t idx1 = *(size_t *)p1; |
|
| 60 |
+ size_t idx2 = *(size_t *)p2; |
|
| 61 |
+ |
|
| 62 |
+ double score1 = choices_score[idx1]; |
|
| 63 |
+ double score2 = choices_score[idx2]; |
|
| 64 |
+ |
|
| 65 |
+ if(score1 == score2) |
|
| 66 |
+ return 0; |
|
| 67 |
+ else if(score1 < score2) |
|
| 68 |
+ return 1; |
|
| 69 |
+ else |
|
| 70 |
+ return -1; |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+ |
|
| 51 | 74 |
void run_search(char *needle){
|
| 75 |
+ choices_available = 0; |
|
| 52 | 76 |
int i; |
| 53 | 77 |
for(i = 0; i < choices_n; i++){
|
| 54 |
- double rank = match(needle, choices[i]); |
|
| 55 |
- if(rank > 0){
|
|
| 56 |
- printf("%s\n", choices[i]);
|
|
| 78 |
+ choices_score[i] = match(needle, choices[i]); |
|
| 79 |
+ if(choices_score[i] >= 0.0){
|
|
| 80 |
+ choices_sorted[choices_available++] = i; |
|
| 57 | 81 |
} |
| 58 | 82 |
} |
| 83 |
+ |
|
| 84 |
+ qsort(choices_sorted, choices_available, sizeof(size_t), cmpchoice); |
|
| 85 |
+ |
|
| 59 | 86 |
} |
| 60 | 87 |
|
| 61 | 88 |
int ttyin; |
| ... | ... |
@@ -112,12 +139,10 @@ void draw(){
|
| 112 | 139 |
const char *prompt = "> "; |
| 113 | 140 |
clear(); |
| 114 | 141 |
fprintf(ttyout, "%s%s\n", prompt, search); |
| 115 |
- for(i = 0; line < 10 && i < choices_n; i++){
|
|
| 116 |
- double rank = match(search, choices[i]); |
|
| 117 |
- if(rank > 0){
|
|
| 118 |
- fprintf(ttyout, "%s\n", choices[i]); |
|
| 119 |
- line++; |
|
| 120 |
- } |
|
| 142 |
+ run_search(search); |
|
| 143 |
+ for(i = 0; line < 10 && i < choices_available; i++){
|
|
| 144 |
+ fprintf(ttyout, "%s\n", choices[choices_sorted[i]]); |
|
| 145 |
+ line++; |
|
| 121 | 146 |
} |
| 122 | 147 |
fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1); |
| 123 | 148 |
fprintf(ttyout, "%c%c%iG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
| ... | ... |
@@ -128,18 +153,15 @@ void emit(){
|
| 128 | 153 |
/* ttyout should be flushed before outputting on stdout */ |
| 129 | 154 |
fclose(ttyout); |
| 130 | 155 |
|
| 131 |
- int i; |
|
| 132 |
- for(i = 0; i < choices_n; i++){
|
|
| 133 |
- double rank = match(search, choices[i]); |
|
| 134 |
- if(rank > 0){
|
|
| 135 |
- /* Output the first match */ |
|
| 136 |
- printf("%s\n", choices[i]);
|
|
| 137 |
- exit(EXIT_SUCCESS); |
|
| 138 |
- } |
|
| 156 |
+ run_search(search); |
|
| 157 |
+ if(choices_available){
|
|
| 158 |
+ /* output the first result */ |
|
| 159 |
+ printf("%s\n", choices[choices_sorted[0]]);
|
|
| 160 |
+ }else{
|
|
| 161 |
+ /* No match, output the query instead */ |
|
| 162 |
+ printf("%s\n", search);
|
|
| 139 | 163 |
} |
| 140 | 164 |
|
| 141 |
- /* No match, output the query instead */ |
|
| 142 |
- printf("%s\n", search);
|
|
| 143 | 165 |
exit(EXIT_SUCCESS); |
| 144 | 166 |
} |
| 145 | 167 |
|
| ... | ... |
@@ -7,15 +7,8 @@ |
| 7 | 7 |
#include <fcntl.h> |
| 8 | 8 |
#include <ctype.h> |
| 9 | 9 |
|
| 10 |
-double match(const char *needle, const char *haystack){
|
|
| 11 |
- while(*needle){
|
|
| 12 |
- if(!*haystack) |
|
| 13 |
- return 0.0; |
|
| 14 |
- while(tolower(*needle) == tolower(*haystack++)) |
|
| 15 |
- needle++; |
|
| 16 |
- } |
|
| 17 |
- return 1.0; |
|
| 18 |
-} |
|
| 10 |
+/* from match.c */ |
|
| 11 |
+double match(const char *needle, const char *haystack); |
|
| 19 | 12 |
|
| 20 | 13 |
#define INITIAL_CAPACITY 1 |
| 21 | 14 |
int choices_capacity = 0; |
| ... | ... |
@@ -131,6 +131,22 @@ void draw(){
|
| 131 | 131 |
fflush(ttyout); |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
+void emit(){
|
|
| 135 |
+ int i; |
|
| 136 |
+ for(i = 0; i < choices_n; i++){
|
|
| 137 |
+ double rank = match(search, choices[i]); |
|
| 138 |
+ if(rank > 0){
|
|
| 139 |
+ /* Output the first match */ |
|
| 140 |
+ printf("%s\n", choices[i]);
|
|
| 141 |
+ exit(EXIT_SUCCESS); |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ /* No match, output the query instead */ |
|
| 146 |
+ printf("%s\n", search);
|
|
| 147 |
+ exit(EXIT_SUCCESS); |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 134 | 150 |
void run(){
|
| 135 | 151 |
draw(); |
| 136 | 152 |
char ch; |
| ... | ... |
@@ -153,7 +169,7 @@ void run(){
|
| 153 | 169 |
draw(); |
| 154 | 170 |
}else if(ch == 10){ /* Enter */
|
| 155 | 171 |
clear(); |
| 156 |
- exit(0); |
|
| 172 |
+ emit(); |
|
| 157 | 173 |
}else{
|
| 158 | 174 |
printf("'%c' (%i)\n", ch, ch);
|
| 159 | 175 |
} |
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,182 @@ |
| 1 |
+#include <stdio.h> |
|
| 2 |
+#include <string.h> |
|
| 3 |
+#include <stdlib.h> |
|
| 4 |
+#include <termios.h> |
|
| 5 |
+#include <unistd.h> |
|
| 6 |
+#include <sys/stat.h> |
|
| 7 |
+#include <fcntl.h> |
|
| 8 |
+#include <ctype.h> |
|
| 9 |
+ |
|
| 10 |
+double match(const char *needle, const char *haystack){
|
|
| 11 |
+ while(*needle){
|
|
| 12 |
+ if(!*haystack) |
|
| 13 |
+ return 0.0; |
|
| 14 |
+ while(tolower(*needle) == tolower(*haystack++)) |
|
| 15 |
+ needle++; |
|
| 16 |
+ } |
|
| 17 |
+ return 1.0; |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+#define INITIAL_CAPACITY 1 |
|
| 21 |
+int choices_capacity = 0; |
|
| 22 |
+int choices_n = 0; |
|
| 23 |
+const char **choices = NULL; |
|
| 24 |
+ |
|
| 25 |
+void resize_choices(int new_capacity){
|
|
| 26 |
+ choices = realloc(choices, new_capacity * sizeof(const char *)); |
|
| 27 |
+ int i = choices_capacity; |
|
| 28 |
+ for(; i < new_capacity; i++){
|
|
| 29 |
+ choices[i] = NULL; |
|
| 30 |
+ } |
|
| 31 |
+ choices_capacity = new_capacity; |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+void add_choice(const char *choice){
|
|
| 35 |
+ if(choices_n == choices_capacity){
|
|
| 36 |
+ resize_choices(choices_capacity * 2); |
|
| 37 |
+ } |
|
| 38 |
+ choices[choices_n++] = choice; |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+void read_choices(){
|
|
| 42 |
+ char *line = NULL; |
|
| 43 |
+ size_t len = 0; |
|
| 44 |
+ ssize_t read; |
|
| 45 |
+ |
|
| 46 |
+ while ((read = getline(&line, &len, stdin)) != -1) {
|
|
| 47 |
+ char *nl; |
|
| 48 |
+ if((nl = strchr(line, '\n'))) |
|
| 49 |
+ *nl = '\0'; |
|
| 50 |
+ |
|
| 51 |
+ add_choice(line); |
|
| 52 |
+ |
|
| 53 |
+ line = NULL; |
|
| 54 |
+ } |
|
| 55 |
+ free(line); |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+void run_search(char *needle){
|
|
| 59 |
+ int i; |
|
| 60 |
+ for(i = 0; i < choices_n; i++){
|
|
| 61 |
+ double rank = match(needle, choices[i]); |
|
| 62 |
+ if(rank > 0){
|
|
| 63 |
+ printf("%s\n", choices[i]);
|
|
| 64 |
+ } |
|
| 65 |
+ } |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+int ttyin; |
|
| 69 |
+FILE *ttyout; |
|
| 70 |
+struct termios original_termios; |
|
| 71 |
+ |
|
| 72 |
+void reset_tty(){
|
|
| 73 |
+ tcsetattr(ttyin, TCSANOW, &original_termios); |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+void init_tty(){
|
|
| 77 |
+ ttyin = open("/dev/tty", O_RDONLY);
|
|
| 78 |
+ ttyout = fopen("/dev/tty", "w");
|
|
| 79 |
+ |
|
| 80 |
+ tcgetattr(ttyin, &original_termios); |
|
| 81 |
+ |
|
| 82 |
+ struct termios new_termios = original_termios; |
|
| 83 |
+ |
|
| 84 |
+ new_termios.c_lflag &= ~(ICANON | ECHO); |
|
| 85 |
+ |
|
| 86 |
+ tcsetattr(ttyin, TCSANOW, &new_termios); |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+char ttygetchar(){
|
|
| 90 |
+ char ch; |
|
| 91 |
+ int size = read(ttyin, &ch, 1); |
|
| 92 |
+ if(size < 0){
|
|
| 93 |
+ perror("error reading from tty");
|
|
| 94 |
+ exit(EXIT_FAILURE); |
|
| 95 |
+ }else if(size == 0){
|
|
| 96 |
+ /* EOF */ |
|
| 97 |
+ exit(EXIT_FAILURE); |
|
| 98 |
+ }else{
|
|
| 99 |
+ return ch; |
|
| 100 |
+ } |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+int search_size; |
|
| 104 |
+char search[4096] = {0};
|
|
| 105 |
+ |
|
| 106 |
+void clear(){
|
|
| 107 |
+ fprintf(ttyout, "%c%c0G", 0x1b, '['); |
|
| 108 |
+ int line = 0; |
|
| 109 |
+ while(line++ < 10 + 1){
|
|
| 110 |
+ fprintf(ttyout, "%c%cK\n", 0x1b, '['); |
|
| 111 |
+ } |
|
| 112 |
+ fprintf(ttyout, "%c%c%iA", 0x1b, '[', line-1); |
|
| 113 |
+ fprintf(ttyout, "%c%c0G", 0x1b, '['); |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+void draw(){
|
|
| 117 |
+ int line = 0; |
|
| 118 |
+ int i; |
|
| 119 |
+ const char *prompt = "> "; |
|
| 120 |
+ clear(); |
|
| 121 |
+ fprintf(ttyout, "%s%s\n", prompt, search); |
|
| 122 |
+ for(i = 0; line < 10 && i < choices_n; i++){
|
|
| 123 |
+ double rank = match(search, choices[i]); |
|
| 124 |
+ if(rank > 0){
|
|
| 125 |
+ fprintf(ttyout, "%s\n", choices[i]); |
|
| 126 |
+ line++; |
|
| 127 |
+ } |
|
| 128 |
+ } |
|
| 129 |
+ fprintf(ttyout, "%c%c%iA", 0x1b, '[', line + 1); |
|
| 130 |
+ fprintf(ttyout, "%c%c%iG", 0x1b, '[', strlen(prompt) + strlen(search) + 1); |
|
| 131 |
+ fflush(ttyout); |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+void run(){
|
|
| 135 |
+ draw(); |
|
| 136 |
+ char ch; |
|
| 137 |
+ do {
|
|
| 138 |
+ ch = ttygetchar(); |
|
| 139 |
+ if(isprint(ch)){
|
|
| 140 |
+ /* FIXME: overflow */ |
|
| 141 |
+ search[search_size++] = ch; |
|
| 142 |
+ search[search_size] = '\0'; |
|
| 143 |
+ draw(); |
|
| 144 |
+ }else if(ch == 127){ /* DEL */
|
|
| 145 |
+ if(search_size) |
|
| 146 |
+ search[--search_size] = '\0'; |
|
| 147 |
+ draw(); |
|
| 148 |
+ }else if(ch == 23){ /* C-W */
|
|
| 149 |
+ if(search_size) |
|
| 150 |
+ search[--search_size] = '\0'; |
|
| 151 |
+ while(search_size && !isspace(search[--search_size])) |
|
| 152 |
+ search[search_size] = '\0'; |
|
| 153 |
+ draw(); |
|
| 154 |
+ }else if(ch == 10){ /* Enter */
|
|
| 155 |
+ clear(); |
|
| 156 |
+ exit(0); |
|
| 157 |
+ }else{
|
|
| 158 |
+ printf("'%c' (%i)\n", ch, ch);
|
|
| 159 |
+ } |
|
| 160 |
+ }while(ch != 'q'); |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+void usage(const char *argv0){
|
|
| 164 |
+ fprintf(stderr, "USAGE: %s\n", argv0); |
|
| 165 |
+ exit(EXIT_FAILURE); |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+int main(int argc, char *argv[]){
|
|
| 169 |
+ if(argc != 1){
|
|
| 170 |
+ usage(argv[0]); |
|
| 171 |
+ } |
|
| 172 |
+ atexit(reset_tty); |
|
| 173 |
+ init_tty(reset_tty); |
|
| 174 |
+ |
|
| 175 |
+ resize_choices(INITIAL_CAPACITY); |
|
| 176 |
+ read_choices(); |
|
| 177 |
+ |
|
| 178 |
+ clear(); |
|
| 179 |
+ run(); |
|
| 180 |
+ |
|
| 181 |
+ return 0; |
|
| 182 |
+} |