hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-29 22:30:40 +0200
committerhachem <im@hachem.wtf>2026-09-29 22:30:40 +0200
commite75e6312b3d75238fdbe24787f4fcce0f17f8574 (patch)
tree17c4971248aec9121e098016f8d11fd2679d9044
parent53df19a1244eef75e3e01fd2c3a643d95c4a5b06 (diff)
fix: bugs
-rw-r--r--Makefile6
-rw-r--r--todo.c103
2 files changed, 103 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index 0a5bc8d..a4fb984 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,7 @@ SRC := todo.c
PREFIX ?= /usr/local
bindir := $(DESTDIR)$(PREFIX)/bin
+mandir := $(DESTDIR)$(PREFIX)/share/man/man1
all: $(TARGET)
@@ -39,12 +40,15 @@ $(TARGET): $(SRC)
clean:
rm -rf $(TARGET) $(TARGET).dSYM
-install:
+install: todo.1
make DEBUG=0 SANITIZE=0 clean all
install -d $(bindir)
install -m 755 $(TARGET) $(bindir)/$(TARGET)
+ install -d $(mandir)
+ install -m 644 todo.1 $(mandir)/todo.1
uninstall:
rm -f $(bindir)/$(TARGET)
+ rm -f $(mandir)/todo.1
.PHONY: all clean install uninstall
diff --git a/todo.c b/todo.c
index 2e23fdf..a4084a2 100644
--- a/todo.c
+++ b/todo.c
@@ -44,7 +44,15 @@ enum Mode
MODE_NORMAL,
MODE_INSERT,
MODE_COMMAND,
- MODE_SEARCH
+ MODE_SEARCH,
+ MODE_CONFIRM
+};
+
+enum Pending
+{
+ PENDING_NONE,
+ PENDING_RESET,
+ PENDING_CLEAR
};
enum Key
@@ -63,6 +71,7 @@ static const char* mode_name(enum Mode mode)
case MODE_INSERT: return "INSERT";
case MODE_COMMAND: return "COMMAND";
case MODE_SEARCH: return "SEARCH";
+ case MODE_CONFIRM: return "CONFIRM";
}
return "?";
@@ -388,6 +397,8 @@ static void insert_item(struct List* list, int index);
static struct Item yank_register;
static int yank_full;
+static enum Pending pending_action;
+
static void paste_item(struct List* list, int index, int* cursor)
{
if (list->count >= MAX_ITEMS || !yank_full)
@@ -691,7 +702,7 @@ static void render(const struct List* list, int cursor, int scroll, enum Mode mo
if (is_subtask(item))
{
- int indent = base_indent + SUBTASK_INDENT;
+ int indent = base_indent + number_width + SUBTASK_INDENT;
const char* name = item->text;
if (!(mode == MODE_INSERT && index == cursor))
@@ -734,6 +745,11 @@ static void render(const struct List* list, int cursor, int scroll, enum Mode mo
length += snprintf(buffer + length, sizeof buffer - (size_t) length,
"\x1b[%d;1H%*s%c%s\x1b[K",
rows, LEFT_MARGIN, "", mode == MODE_SEARCH ? '/' : ':', command);
+ else if (mode == MODE_CONFIRM)
+ length += snprintf(buffer + length, sizeof buffer - (size_t) length,
+ "\x1b[%d;1H%*s%s [y/n]\x1b[K",
+ rows, LEFT_MARGIN, "",
+ pending_action == PENDING_CLEAR ? "clear the whole list?" : "reset all items to todo?");
else
length += snprintf(buffer + length, sizeof buffer - (size_t) length,
"\x1b[%d;1H%*s\x1b[2m[%s] %d/%d done\x1b[0m\x1b[K",
@@ -746,7 +762,7 @@ static void render(const struct List* list, int cursor, int scroll, enum Mode mo
length += snprintf(buffer + length, sizeof buffer - (size_t) length,
"\x1b[%d;%dH\x1b[6 q\x1b[?25h", rows, cursor_column);
}
- else if (cursor >= 0 && cursor < list->count)
+ else if (mode != MODE_CONFIRM && cursor >= 0 && cursor < list->count)
{
int rows_before = 0;
@@ -780,7 +796,7 @@ static void render(const struct List* list, int cursor, int scroll, enum Mode mo
int text_start;
if (is_subtask(&list->items[cursor]))
- text_start = base_indent + SUBTASK_INDENT + 5;
+ text_start = base_indent + number_width + SUBTASK_INDENT + 5;
else
text_start = base_indent + number_width + 7;
@@ -1291,6 +1307,14 @@ static int command_key(struct List* list, const char* path, enum Mode* mode, cha
if (key == '\r' || key == '\n')
{
+ if (strcmp(command, "reset") == 0 || strcmp(command, "clear") == 0)
+ {
+ pending_action = strcmp(command, "clear") == 0 ? PENDING_CLEAR : PENDING_RESET;
+ command[0] = '\0';
+ *mode = MODE_CONFIRM;
+ return 1;
+ }
+
int write = strchr(command, 'w') != NULL || strcmp(command, "x") == 0;
int quit = strchr(command, 'q') != NULL || strcmp(command, "x") == 0;
@@ -1368,6 +1392,30 @@ static void search_key(const struct List* list, int* cursor, enum Mode* mode, ch
}
}
+static void confirm_key(struct List* list, enum Mode* mode, int key)
+{
+ if (key == 'y' || key == 'Y')
+ {
+ history_record(list);
+
+ if (pending_action == PENDING_RESET)
+ {
+ for (int i = 0; i < list->count; i++)
+ if (!is_section(&list->items[i]))
+ list->items[i].state = STATE_TODO;
+ }
+ else if (pending_action == PENDING_CLEAR)
+ {
+ list->count = 0;
+ }
+
+ commit(list);
+ }
+
+ pending_action = PENDING_NONE;
+ *mode = MODE_NORMAL;
+}
+
static void run(struct List* list, const char* path)
{
if (enter_raw_mode() != 0)
@@ -1449,12 +1497,57 @@ static void run(struct List* list, const char* path)
case MODE_SEARCH:
search_key(list, &cursor, &mode, command, search, key);
break;
+
+ case MODE_CONFIRM:
+ confirm_key(list, &mode, key);
+ break;
}
}
}
-int main(void)
+static void print_version(void)
+{
+ (void) printf("%s %s\n", TODO_PROGRAM, TODO_VERSION);
+}
+
+static void print_help(void)
{
+ (void) printf(
+ "%s %s - a little modal todo list\n"
+ "\n"
+ "usage: %s open the list ($TODO_FILE, or ~/.todo.txt)\n"
+ " %s -h | --help\n"
+ " %s -v | --version\n"
+ "\n"
+ "keys:\n"
+ " h j k l / arrows move w / b words { / } sections\n"
+ " gg / G top/bottom o new item i insert at cursor\n"
+ " A append text R replace text 3j 2dd counts\n"
+ " x toggle done space cycle state\n"
+ " dd cut item D clear done yy p P yank/paste\n"
+ " J / K move item or section / n N search u ctrl-r undo/redo\n"
+ " :w :q :wq :x write / quit :reset all back to todo :clear empty\n"
+ "\n"
+ "items: '= name' section, '== name' subsection, '- name' subtask, else a task.\n"
+ "\n"
+ "by hachem <im@hachem.wtf>\n",
+ TODO_PROGRAM, TODO_VERSION, TODO_PROGRAM, TODO_PROGRAM, TODO_PROGRAM);
+}
+
+int main(int argc, char** argv)
+{
+ if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))
+ {
+ print_help();
+ return EXIT_SUCCESS;
+ }
+
+ if (argc >= 2 && (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0))
+ {
+ print_version();
+ return EXIT_SUCCESS;
+ }
+
(void) setlocale(LC_ALL, "");
char path[4096];