diff options
| author | hachem <im@hachem.wtf> | 2026-09-29 19:30:05 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-29 19:30:05 +0200 |
| commit | f730ea320875540679908d77696b211be55c4b57 (patch) | |
| tree | 0914262170b84371cdf2f040c3779485be5f2a43 | |
| parent | cdc7348d5fee6a82036ad0b1cb5b3038591cf7ee (diff) | |
fix: make the keybinds a bit more consistant
| -rw-r--r-- | todo.c | 234 |
1 files changed, 207 insertions, 27 deletions
@@ -43,7 +43,8 @@ enum Mode { MODE_NORMAL, MODE_INSERT, - MODE_COMMAND + MODE_COMMAND, + MODE_SEARCH }; enum Key @@ -61,6 +62,7 @@ static const char* mode_name(enum Mode mode) case MODE_NORMAL: return "NORMAL"; case MODE_INSERT: return "INSERT"; case MODE_COMMAND: return "COMMAND"; + case MODE_SEARCH: return "SEARCH"; } return "?"; @@ -381,6 +383,70 @@ static void history_redo(struct List* list) free(snap.items); } +static void insert_item(struct List* list, int index); + +static struct Item yank_register; +static int yank_full; + +static void paste_item(struct List* list, int index, int* cursor) +{ + if (list->count >= MAX_ITEMS || !yank_full) + return; + + history_record(list); + insert_item(list, index); + + if (index >= 0 && index < list->count) + { + list->items[index] = yank_register; + *cursor = index; + } +} + +static char lower(char c) +{ + return c >= 'A' && c <= 'Z' ? (char) (c + 32) : c; +} + +static int contains_fold(const char* haystack, const char* needle) +{ + if (needle[0] == '\0') + return 0; + + for (; *haystack != '\0'; haystack++) + { + const char* h = haystack; + const char* n = needle; + + while (*h != '\0' && *n != '\0' && lower(*h) == lower(*n)) + { + h++; + n++; + } + + if (*n == '\0') + return 1; + } + + return 0; +} + +static int find_match(const struct List* list, int from, int direction, const char* query) +{ + if (list->count == 0 || query[0] == '\0') + return -1; + + for (int step = 1; step <= list->count; step++) + { + int index = ((from + direction * step) % list->count + list->count) % list->count; + + if (contains_fold(list->items[index].text, query)) + return index; + } + + return -1; +} + static void insert_item(struct List* list, int index) { if (list->count >= MAX_ITEMS) @@ -664,15 +730,16 @@ static void render(const struct List* list, int cursor, int scroll, enum Mode mo emitted++; } - if (mode == MODE_COMMAND) + if (mode == MODE_COMMAND || mode == MODE_SEARCH) length += snprintf(buffer + length, sizeof buffer - (size_t) length, - "\x1b[%d;1H%*s:%s\x1b[K", rows, LEFT_MARGIN, "", command); + "\x1b[%d;1H%*s%c%s\x1b[K", + rows, LEFT_MARGIN, "", mode == MODE_SEARCH ? '/' : ':', command); else length += snprintf(buffer + length, sizeof buffer - (size_t) length, "\x1b[%d;1H%*s\x1b[2m[%s] %d/%d done\x1b[0m\x1b[K", rows, LEFT_MARGIN, "", mode_name(mode), done, total_items); - if (mode == MODE_COMMAND) + if (mode == MODE_COMMAND || mode == MODE_SEARCH) { int cursor_column = LEFT_MARGIN + 2 + (int) strlen(command); @@ -789,36 +856,81 @@ static int read_key(void) return 27; } -static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit_pos, char* command, int key) +static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit_pos, + char* command, const char* search, int count, int key) { switch (key) { - case 'q': - return 0; - case ':': command[0] = '\0'; *mode = MODE_COMMAND; break; + case '/': + command[0] = '\0'; + *mode = MODE_SEARCH; + break; + + case 'n': + { + int match = find_match(list, *cursor, 1, search); + + if (match >= 0) + *cursor = match; + + break; + } + + case 'N': + { + int match = find_match(list, *cursor, -1, search); + + if (match >= 0) + *cursor = match; + + break; + } + + case 'y': + { + char next; + + if (read(STDIN_FILENO, &next, 1) == 1 && next == 'y' + && *cursor >= 0 && *cursor < list->count) + { + yank_register = list->items[*cursor]; + yank_full = 1; + } + + break; + } + + case 'p': + paste_item(list, *cursor + 1, cursor); + break; + + case 'P': + paste_item(list, *cursor, cursor); + break; + case 'j': case KEY_DOWN: - (*cursor)++; + *cursor += count; break; case 'k': case KEY_UP: - (*cursor)--; + *cursor -= count; break; case 'h': case KEY_LEFT: - (*edit_pos)--; + *edit_pos -= count; break; case 'l': case KEY_RIGHT: - (*edit_pos)++; + *edit_pos += count; break; case 'w': @@ -828,11 +940,14 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit int length = (int) strlen(text); int position = *edit_pos; - while (position < length && text[position] != ' ') - position++; + for (int repeat = 0; repeat < count; repeat++) + { + while (position < length && text[position] != ' ') + position++; - while (position < length && text[position] == ' ') - position++; + while (position < length && text[position] == ' ') + position++; + } *edit_pos = position; } @@ -845,14 +960,17 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit const char* text = list->items[*cursor].text; int position = *edit_pos; - if (position > 0) - position--; + for (int repeat = 0; repeat < count; repeat++) + { + if (position > 0) + position--; - while (position > 0 && text[position] == ' ') - position--; + while (position > 0 && text[position] == ' ') + position--; - while (position > 0 && text[position - 1] != ' ') - position--; + while (position > 0 && text[position - 1] != ' ') + position--; + } *edit_pos = position; } @@ -889,7 +1007,6 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit break; - case 'a': case 'o': history_record(list); insert_item(list, *cursor + 1); @@ -993,10 +1110,16 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit { char next; - if (read(STDIN_FILENO, &next, 1) == 1 && next == 'd') + if (read(STDIN_FILENO, &next, 1) == 1 && next == 'd' + && *cursor >= 0 && *cursor < list->count) { history_record(list); - delete_item(list, *cursor); + yank_register = list->items[*cursor]; + yank_full = 1; + + for (int repeat = 0; repeat < count && *cursor < list->count; repeat++) + delete_item(list, *cursor); + commit(list); } @@ -1203,6 +1326,48 @@ static int command_key(struct List* list, const char* path, enum Mode* mode, cha return 1; } +static void search_key(const struct List* list, int* cursor, enum Mode* mode, char* command, char* search, int key) +{ + size_t length = strlen(command); + + if (key == 27) + { + command[0] = '\0'; + *mode = MODE_NORMAL; + return; + } + + if (key == '\r' || key == '\n') + { + (void) snprintf(search, MAX_COMMAND, "%s", command); + command[0] = '\0'; + *mode = MODE_NORMAL; + + int match = find_match(list, *cursor, 1, search); + + if (match >= 0) + *cursor = match; + + return; + } + + if (key == 127 || key == 8) + { + if (length > 0) + command[length - 1] = '\0'; + else + *mode = MODE_NORMAL; + + return; + } + + if (key >= 32 && key <= 126 && length + 1 < MAX_COMMAND) + { + command[length] = (char) key; + command[length + 1] = '\0'; + } +} + static void run(struct List* list, const char* path) { if (enter_raw_mode() != 0) @@ -1215,7 +1380,9 @@ static void run(struct List* list, const char* path) int cursor = 0; int scroll = 0; int edit_pos = 0; + int count = 0; char command[MAX_COMMAND] = ""; + char search[MAX_COMMAND] = ""; for (;;) { @@ -1255,9 +1422,18 @@ static void run(struct List* list, const char* path) switch (mode) { case MODE_NORMAL: - if (normal_key(list, &cursor, &mode, &edit_pos, command, key) == 0) - return; + if ((key >= '1' && key <= '9') || (key == '0' && count > 0)) + { + count = count * 10 + (key - '0'); + + if (count > 9999) + count = 9999; + break; + } + + normal_key(list, &cursor, &mode, &edit_pos, command, search, count > 0 ? count : 1, key); + count = 0; break; case MODE_INSERT: @@ -1269,6 +1445,10 @@ static void run(struct List* list, const char* path) return; break; + + case MODE_SEARCH: + search_key(list, &cursor, &mode, command, search, key); + break; } } } |
