hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-29 19:30:05 +0200
committerhachem <im@hachem.wtf>2026-09-29 19:30:05 +0200
commitf730ea320875540679908d77696b211be55c4b57 (patch)
tree0914262170b84371cdf2f040c3779485be5f2a43
parentcdc7348d5fee6a82036ad0b1cb5b3038591cf7ee (diff)
fix: make the keybinds a bit more consistant
-rw-r--r--todo.c234
1 files changed, 207 insertions, 27 deletions
diff --git a/todo.c b/todo.c
index d4b09ce..2e23fdf 100644
--- a/todo.c
+++ b/todo.c
@@ -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;
}
}
}