#include <vec.h>
#include <termbox.h>
#include <ctype.h>
+#include <math.h>
+
+static void redraw(void);
+static void filter(void);
typedef struct {
float score;
char* ARGV0;
int AltFlag = 1; // Disable alternate screen usage
-char Query[1024] = {0};
+char Query[8192] = {0};
size_t QueryIdx = 0;
vec_t Choices = {0};
-size_t ChoiceIdx = 2;
+size_t ChoiceIdx = 0;
+
+static char* rdline(FILE* fin, size_t* len) {
+ if (feof(fin) || ferror(fin))
+ return NULL;
+ size_t size = 256;
+ size_t index = 0;
+ char* str = (char*)calloc(size,1);
+ while (!feof(stdin)) {
+ if (index+2 >= size) {
+ size = size << 1;
+ str = realloc(str, size);
+ }
+ char ch = fgetc(fin);
+ if (ch == EOF || ch == '\r' || ch == '\n') break;
+ str[index++] = ch;
+ str[index] = '\0';
+ }
+ if (len) *len = index;
+ return str;
+}
-int by_score(const void* a, const void* b) {
+static int by_score(const void* a, const void* b) {
Choice* ca = ((Choice*)a);
Choice* cb = ((Choice*)b);
if (ca->score < cb->score)
return strcmp(ca->string, cb->string);
}
-void load_choices(void) {
+static void load_choices(void) {
+ size_t choice_len;
char* choice_text;
Choice choice = {0};
vec_init(&Choices, sizeof(Choice));
- while ((choice_text = rdline(stdin)) != NULL) {
- choice_text[strlen(choice_text)-1] = '\0';
- if (strlen(choice_text) > 0) {
+ while ((choice_text = rdline(stdin, &choice_len)) != NULL) {
+ if (choice_len > 0) {
choice.string = choice_text;
choice.length = strlen(choice_text);
choice.score = 1.0;
vec_push_back(&Choices, &choice);
+ } else {
+ free(choice_text);
}
}
- vec_sort(&Choices, by_score);
}
-char* find_match_start(char *str, int ch) {
+static char* find_match_start(char *str, int ch) {
for (; *str; str++)
if (tolower(*str) == tolower(ch))
return str;
return NULL;
}
-bool match(char *string, size_t offset, size_t *start, size_t *end) {
+static bool match(char *string, size_t offset, size_t *start, size_t *end) {
char* q = Query;
char* s = find_match_start(&string[offset], *q);
char* e = s;
/* bail if no match for first char */
if (s == NULL) return 0;
/* find the end of the match */
- for (; *q; q++)
+ for (q++, e++; *q; q++)
if ((e = find_match_start(e, *q)) == NULL)
return false;
/* make note of the matching range */
return true;
}
-void score(void) {
- for (int i = 0; i < vec_size(&Choices); i++) {
+static void score(void) {
+ for (unsigned int i = 0; i < vec_size(&Choices); i++) {
Choice* choice = (Choice*)vec_at(&Choices, i);
float qlen = (float)QueryIdx;
if (match(choice->string, 0, &choice->match_start, &choice->match_end)) {
vec_sort(&Choices, by_score);
}
-void redraw(void) {
+int main(int argc, char** argv) {
+ OPTBEGIN {
+ case 'a': AltFlag = 0; break;
+ } OPTEND;
+ if (argc >= 1) {
+ size_t sz = min(strlen(argv[0]), sizeof(Query)-1);
+ strncpy(Query, argv[0], sz);
+ QueryIdx = sz;
+ }
+ load_choices();
+ score();
+ if (vec_size(&Choices) > 1)
+ filter();
+ Choice* choice = (Choice*)vec_at(&Choices, ChoiceIdx);
+ if (vec_size(&Choices) && ChoiceIdx != SIZE_MAX)
+ printf("%s\n", choice->string);
+ return 0;
+}
+
+static void redraw(void) {
tb_clear();
/* Draw query and cursor */
int max_x = (tb_width() < 1023 ? tb_width() : 1023);
for (int x = 0; x < tb_width(); x++)
tb_change_cell(x, 1, 0x2500, TB_DEFAULT, TB_DEFAULT);
/* Draw the scored and sorted results */
- for (int i = 0, y = 2; (i < vec_size(&Choices)) && (y < tb_height()); i++) {
- bool selected = (y == ChoiceIdx);
+ for (int i = 0, y = 2; (i < vec_size(&Choices)) && (y < tb_height()); i++, y++) {
+ bool selected = (i == ChoiceIdx);
Choice* choice = (Choice*)vec_at(&Choices, i);
if (choice->score >= 0.0) {
for (int x = 0; choice->string[x] && x < tb_width(); x++) {
bool inmatch = (choice->match_end && x >= choice->match_start && x <= choice->match_end);
- tb_change_cell(x, i+2, choice->string[x],
+ tb_change_cell(x, y, choice->string[x],
(inmatch ? TB_UNDERLINE|TB_BLUE : TB_DEFAULT),
(selected ? TB_WHITE : TB_DEFAULT));
}
for (int x = choice->length; selected && (x < tb_width()); x++)
tb_change_cell(x, y, ' ', TB_DEFAULT, TB_WHITE);
- y++;
}
}
tb_present();
}
-void filter(void) {
+static void filter(void) {
struct tb_event ev = {0};
tb_init_with(AltFlag ? TB_INIT_EVERYTHING : 0);
do {
if (QueryIdx > 0)
Query[--QueryIdx] = '\0';
} else if (ev.key == TB_KEY_ARROW_DOWN) {
- if (ChoiceIdx < tb_width() && ChoiceIdx <= vec_size(&Choices))
+ if (ChoiceIdx < tb_width() && ChoiceIdx < vec_size(&Choices)-1)
ChoiceIdx++;
} else if (ev.key == TB_KEY_ARROW_UP) {
- if (ChoiceIdx > 2)
+ if (ChoiceIdx > 0)
ChoiceIdx--;
} else if (ev.ch) {
if (QueryIdx < sizeof(Query)-1)
} while (tb_poll_event(&ev));
tb_shutdown();
}
-
-int main(int argc, char** argv) {
- OPTBEGIN {
- case 'a': AltFlag = 0; break;
- } OPTEND;
- load_choices();
- if (vec_size(&Choices) > 1)
- filter();
- Choice* choice = (Choice*)vec_at(&Choices, ChoiceIdx-2);
- if (vec_size(&Choices) && ChoiceIdx != SIZE_MAX)
- printf("%s\n", choice->string);
- return 0;
-}