hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-10-03 13:44:11 +0200
committerhachem <im@hachem.wtf>2026-10-03 13:44:11 +0200
commit2981b6b3bbd9d40aee6c0d23d450dcd58a00adac (patch)
treee38002ae53310d7aaaa5416a41e0bbc026c87671
parent908e6d88f32c6eb1855d13156fad301c402a1798 (diff)
fix: code that i wrote when i was piss drunk
-rw-r--r--README.md1
-rw-r--r--todo.17
-rw-r--r--todo.c1115
3 files changed, 632 insertions, 491 deletions
diff --git a/README.md b/README.md
index dced413..a66a563 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ a little modal todo list that lives in my terminal.
c0 c$ cc change to start / end of line, or the whole line
ct{c} cf{c} change up to (cf: onto) the next {c}
ci{d} ca{d} change inside / around a pair: () [] {} <> " '
+ dw de db d0 d$ delete over the same motions (also dt df di da)
space cycle todo -> doing -> done
dd cut the item (with its subtasks and notes)
D clear every done item
diff --git a/todo.1 b/todo.1
index 2047f23..caa3133 100644
--- a/todo.1
+++ b/todo.1
@@ -148,6 +148,13 @@ Cycle the item through todo, doing and done.
Cut the item (also copies it for pasting). A task takes its subtasks and notes
with it.
.TP
+.BR dw ", " de ", " db ", " d0 ", " d$ "\fR, etc.\fP"
+Delete over a motion, leaving normal mode. The same motions and text objects as
+.B c
+apply
+.RB ( dw ", " de ", " db ", " d0 ", " d$ ", " "dt\fIc\fP" ", " "df\fIc\fP" ", " "di\fId\fP" ", " "da\fId\fP" ),
+with counts.
+.TP
.B D
Remove every done item.
.TP
diff --git a/todo.c b/todo.c
index 1aca5e9..2db8cb2 100644
--- a/todo.c
+++ b/todo.c
@@ -58,15 +58,27 @@ enum Key
KEY_RIGHT
};
+struct Editor
+{
+ int cursor;
+ int scroll;
+ int edit_pos;
+ int count;
+ enum Mode mode;
+ char command[MAX_COMMAND];
+ char search[MAX_COMMAND];
+ char message[MAX_COMMAND];
+};
+
static const char* mode_name(enum Mode mode)
{
switch (mode)
{
- case MODE_NORMAL: return "NORMAL";
- case MODE_INSERT: return "INSERT";
+ case MODE_NORMAL: return "NORMAL";
+ case MODE_INSERT: return "INSERT";
case MODE_REPLACE: return "REPLACE";
case MODE_COMMAND: return "COMMAND";
- case MODE_SEARCH: return "SEARCH";
+ case MODE_SEARCH: return "SEARCH";
case MODE_CONFIRM: return "CONFIRM";
}
@@ -75,7 +87,8 @@ static const char* mode_name(enum Mode mode)
static int is_editing(enum Mode mode)
{
- return mode == MODE_INSERT || mode == MODE_REPLACE;
+ return mode == MODE_INSERT ||
+ mode == MODE_REPLACE;
}
static int utf8_width(const char* text, int bytes)
@@ -89,32 +102,39 @@ static int utf8_width(const char* text, int bytes)
return width;
}
-static int utf8_left(const char* text, int pos)
+static int utf8_left(const char* text, int position)
{
- if (pos <= 0)
+ if (position <= 0)
return 0;
- pos--;
+ position--;
- while (pos > 0 && ((unsigned char) text[pos] & 0xC0) == 0x80)
- pos--;
+ while (position > 0 && ((unsigned char) text[position] & 0xC0) == 0x80)
+ position--;
- return pos;
+ return position;
}
-static int utf8_right(const char* text, int pos)
+static int utf8_right(const char* text, int position)
{
int length = (int) strlen(text);
- if (pos >= length)
+ if (position >= length)
return length;
- pos++;
+ position++;
- while (pos < length && ((unsigned char) text[pos] & 0xC0) == 0x80)
- pos++;
+ while (position < length && ((unsigned char) text[position] & 0xC0) == 0x80)
+ position++;
- return pos;
+ return position;
+}
+
+static int caret_cols(const char* name, const char* text, int edit_pos)
+{
+ int offset = (int) (name - text);
+ int byte = edit_pos - offset < 0 ? 0 : edit_pos - offset;
+ return utf8_width(name, byte);
}
static void todo_path(char* buffer, size_t size)
@@ -151,7 +171,8 @@ static void load(struct List* list, const char* path)
char line[MAX_TEXT + 8];
- while (list->count < MAX_ITEMS && fgets(line, sizeof line, file) != NULL)
+ while (list->count < MAX_ITEMS &&
+ fgets(line, sizeof line, file) != NULL)
{
strip_newline(line);
@@ -202,24 +223,30 @@ static int is_note(const struct Item* item)
return item->text[0] == '>';
}
+static int is_child(const struct Item* item)
+{
+ return is_subtask(item) || is_note(item);
+}
+
+static int is_task(const struct Item* item)
+{
+ return !is_section(item) && !is_child(item);
+}
+
static int task_group(const struct Item* item)
{
if (item->state == STATE_DONE)
return 2;
-
if (is_optional(item))
return 1;
-
return 0;
}
static int section_level(const struct Item* item)
{
int level = 0;
-
while (item->text[level] == '=')
level++;
-
return level;
}
@@ -234,7 +261,6 @@ static int text_start(const struct Item* item)
while (item->text[i] == ' ')
i++;
-
return i;
}
@@ -242,7 +268,7 @@ static void normalize_tasks(struct List* list)
{
for (int i = 0; i < list->count; i++)
{
- if (is_section(&list->items[i]) || is_subtask(&list->items[i]) || is_note(&list->items[i]))
+ if (!is_task(&list->items[i]))
continue;
int total = 0;
@@ -250,7 +276,7 @@ static void normalize_tasks(struct List* list)
int active = 0;
for (int j = i + 1;
- j < list->count && (is_subtask(&list->items[j]) || is_note(&list->items[j]));
+ j < list->count && is_child(&list->items[j]);
j++)
{
if (!is_subtask(&list->items[j]))
@@ -308,8 +334,7 @@ static void sort_list(struct List* list)
{
int block = k + 1;
- while (block < segment
- && (is_subtask(&list->items[block]) || is_note(&list->items[block])))
+ while (block < segment && is_child(&list->items[block]))
block++;
if (task_group(&list->items[k]) == group)
@@ -357,31 +382,23 @@ static void sort_list(struct List* list)
static int task_block_end(const struct List* list, int start)
{
int i = start + 1;
-
- while (i < list->count && (is_subtask(&list->items[i]) || is_note(&list->items[i])))
+ while (i < list->count && is_child(&list->items[i]))
i++;
-
return i;
}
static int item_block_end(const struct List* list, int start)
{
- if (start >= 0 && start < list->count
- && !is_section(&list->items[start])
- && !is_subtask(&list->items[start])
- && !is_note(&list->items[start]))
+ if (start >= 0 && start < list->count && is_task(&list->items[start]))
return task_block_end(list, start);
-
return start + 1;
}
static int block_end(const struct List* list, int start)
{
int i = start + 1;
-
while (i < list->count && !is_section(&list->items[i]))
i++;
-
return i;
}
@@ -414,7 +431,7 @@ static void save(struct List* list, const char* path)
continue;
}
- char marker = list->items[i].state == STATE_DONE ? 'x'
+ char marker = list->items[i].state == STATE_DONE ? 'x'
: list->items[i].state == STATE_DOING ? '-'
: ' ';
@@ -522,7 +539,7 @@ static enum Pending pending_action;
static char replace_original[MAX_TEXT];
static int replace_start;
-static void paste_item(struct List* list, int index, int* cursor)
+static void paste_item(struct List* list, struct Editor* editor, int index)
{
if (yank_count <= 0 || list->count + yank_count > MAX_ITEMS)
return;
@@ -541,12 +558,12 @@ static void paste_item(struct List* list, int index, int* cursor)
for (int i = 0; i < yank_count; i++)
list->items[index + i] = yank_register[i];
- *cursor = index;
+ editor->cursor = index;
}
-static char lower(char c)
+static char lower(char character)
{
- return c >= 'A' && c <= 'Z' ? (char) (c + 32) : c;
+ return character >= 'A' && character <= 'Z' ? (char) (character + 32) : character;
}
static int contains_fold(const char* haystack, const char* needle)
@@ -572,13 +589,13 @@ static int contains_fold(const char* haystack, const char* needle)
return 0;
}
-static int match_at(const char* text, int pos, const char* needle)
+static int match_at(const char* text, int position, const char* needle)
{
int k = 0;
-
- while (needle[k] != '\0' && text[pos + k] != '\0' && lower(text[pos + k]) == lower(needle[k]))
+ while (needle[k] != '\0' &&
+ text[position + k] != '\0' &&
+ lower(text[position + k]) == lower(needle[k]))
k++;
-
return needle[k] == '\0' ? k : 0;
}
@@ -586,11 +603,9 @@ static int match_offset(const char* text, const char* needle)
{
if (needle[0] == '\0')
return -1;
-
for (int i = 0; text[i] != '\0'; i++)
if (match_at(text, i, needle) > 0)
return i;
-
return -1;
}
@@ -610,6 +625,20 @@ static int find_match(const struct List* list, int from, int direction, const ch
return -1;
}
+static void goto_match(const struct List* list, struct Editor* editor, int direction)
+{
+ int match = find_match(list, editor->cursor, direction, editor->search);
+
+ if (match < 0)
+ return;
+
+ editor->cursor = match;
+ int offset = match_offset(list->items[match].text, editor->search);
+
+ if (offset >= 0)
+ editor->edit_pos = offset;
+}
+
static void insert_item(struct List* list, int index)
{
if (list->count >= MAX_ITEMS)
@@ -640,7 +669,6 @@ static void delete_item(struct List* list, int index)
list->count--;
}
-
static struct termios saved_termios;
static void restore_terminal(void)
@@ -688,37 +716,57 @@ static int enter_raw_mode(void)
static int screen_rows(void)
{
struct winsize window;
-
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window) == 0 && window.ws_row > 0)
return window.ws_row;
-
return 24;
}
static int screen_cols(void)
{
struct winsize window;
-
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window) == 0 && window.ws_col > 0)
return window.ws_col;
-
return 80;
}
static int content_rows(void)
{
int rows = screen_rows() - 4;
-
return rows < 1 ? 1 : rows;
}
-static int emit_wrapped(char* buffer, int length, int cols,
- const char* background, const char* background_reset,
- const char* prefix, int prefix_cols, int first_col,
- const char* style, const char* style_reset,
- const char* text, const char* search, int* emitted, int visible,
- int is_cursor, int caret_cols, int* cursor_row, int* cursor_column)
+struct Line
{
+ int cols;
+ const char* background;
+ const char* background_reset;
+ const char* prefix;
+ int prefix_cols;
+ int first_col;
+ const char* style;
+ const char* style_reset;
+ const char* text;
+ const char* search;
+ int is_cursor;
+ int caret;
+};
+
+static int emit_wrapped(char* buffer, int length, const struct Line* spec,
+ int* emitted, int visible, int* cursor_row, int* cursor_column)
+{
+ int cols = spec->cols;
+ const char* background = spec->background;
+ const char* background_reset = spec->background_reset;
+ const char* prefix = spec->prefix;
+ int prefix_cols = spec->prefix_cols;
+ int first_col = spec->first_col;
+ const char* style = spec->style;
+ const char* style_reset = spec->style_reset;
+ const char* text = spec->text;
+ const char* search = spec->search;
+ int is_cursor = spec->is_cursor;
+ int caret_cols = spec->caret;
+
int total = (int) strlen(text);
int byte = 0;
int line = 0;
@@ -830,7 +878,8 @@ static int emit_wrapped(char* buffer, int length, int cols,
static void to_roman(int number, char* buffer, size_t size)
{
static const int values[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
- static const char* const symbols[] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
+ static const char* const symbols[] = { "M", "CM", "D", "CD", "C", "XC", "L",
+ "XL", "X", "IX", "V", "IV", "I" };
size_t length = 0;
buffer[0] = '\0';
@@ -859,7 +908,7 @@ static int compute_number_width(const struct List* list)
{
if (is_section(&list->items[i]))
run = 0;
- else if (!is_subtask(&list->items[i]) && !is_note(&list->items[i]) && ++run > max_run)
+ else if (!is_child(&list->items[i]) && ++run > max_run)
max_run = run;
}
@@ -871,19 +920,28 @@ static int compute_number_width(const struct List* list)
return number_width;
}
-static int render(const struct List* list, int cursor, int scroll, enum Mode mode, int edit_pos, const char* command, const char* search, int measure)
+static void build_line(const struct List* list, int index, int number_width, int raw,
+ char* prefix, size_t prefix_size, struct Line* line);
+
+static int render(const struct List* list, const struct Editor* editor, int measure)
{
+ int cursor = editor->cursor;
+ int scroll = editor->scroll;
+ enum Mode mode = editor->mode;
+ int edit_pos = editor->edit_pos;
+ const char* command = editor->command;
+
int rows = screen_rows();
int cols = wrap_column();
int visible = content_rows();
- const char* matches = mode == MODE_SEARCH ? command : search;
+ const char* matches = mode == MODE_SEARCH ? command : editor->search;
int total_items = 0;
int done = 0;
for (int i = 0; i < list->count; i++)
- if (!is_section(&list->items[i]) && !is_subtask(&list->items[i]) && !is_note(&list->items[i]))
+ if (is_task(&list->items[i]))
{
total_items++;
done += list->items[i].state == STATE_DONE;
@@ -985,73 +1043,7 @@ static int render(const struct List* list, int cursor, int scroll, enum Mode mod
continue;
}
- int heading_level = 0;
-
- for (int k = index - 1; k >= 0; k--)
- if (is_section(&list->items[k]))
- {
- heading_level = section_level(&list->items[k]);
- break;
- }
-
- int base_indent = LEFT_MARGIN + heading_level * SECTION_INDENT;
-
- if (is_note(item))
- {
- int indent = base_indent + number_width + 6;
- const char* name = item->text;
-
- char prefix[16];
- (void) snprintf(prefix, sizeof prefix, "%*s", indent, "");
-
- int caret = 0;
-
- if (index == cursor)
- caret = utf8_width(name, edit_pos);
-
- length = emit_wrapped(buffer, length, cols, background, background_reset,
- prefix, indent + 2, indent, "\x1b[2;3m", "\x1b[22;23m", name,
- matches, &emitted, visible, index == cursor, caret, &cursor_row, &cursor_column);
- index++;
- continue;
- }
-
- const char* box = item->state == STATE_DONE ? "[x]"
- : item->state == STATE_DOING ? "[-]"
- : "[ ]";
- const char* dim = item->state == STATE_DONE ? "\x1b[2m" : "";
- const char* dim_reset = item->state == STATE_DONE ? "\x1b[22m" : "";
-
- if (is_subtask(item))
- {
- int indent = base_indent + number_width + SUBTASK_INDENT;
- const char* name = item->text;
-
- if (!(is_editing(mode) && index == cursor))
- {
- name = item->text + 1;
-
- while (*name == ' ')
- name++;
- }
-
- char prefix[32];
- (void) snprintf(prefix, sizeof prefix, "%*s%s ", indent, "", box);
-
- int caret = 0;
-
- if (index == cursor)
- {
- int offset = (int) (name - item->text);
- int byte = edit_pos - offset < 0 ? 0 : edit_pos - offset;
- caret = utf8_width(name, byte);
- }
-
- length = emit_wrapped(buffer, length, cols, background, background_reset,
- prefix, indent + 4, indent + 4, dim, dim_reset, name,
- matches, &emitted, visible, index == cursor, caret, &cursor_row, &cursor_column);
- }
- else
+ if (is_task(item))
{
int group = task_group(item);
@@ -1065,45 +1057,21 @@ static int render(const struct List* list, int cursor, int scroll, enum Mode mod
}
prev_group = group;
+ }
- int number = 0;
-
- for (int k = index; k >= 0 && !is_section(&list->items[k]); k--)
- if (!is_subtask(&list->items[k]) && !is_note(&list->items[k]))
- number++;
-
- const char* task_box = is_optional(item)
- ? (item->state == STATE_DONE ? "(x)" : item->state == STATE_DOING ? "(-)" : "( )")
- : box;
- const char* name = item->text;
-
- if (is_optional(item) && !(is_editing(mode) && index == cursor))
- {
- name = item->text + 1;
-
- while (*name == ' ')
- name++;
- }
-
- char prefix[48];
- (void) snprintf(prefix, sizeof prefix, "%*s\x1b[2m%*d.\x1b[22m %s ",
- base_indent, "", number_width, number, task_box);
+ char prefix[48];
+ struct Line line = { 0 };
+ build_line(list, index, number_width, is_editing(mode) && index == cursor,
+ prefix, sizeof prefix, &line);
- int caret = 0;
-
- if (index == cursor)
- {
- int offset = (int) (name - item->text);
- int byte = edit_pos - offset < 0 ? 0 : edit_pos - offset;
- caret = utf8_width(name, byte);
- }
-
- length = emit_wrapped(buffer, length, cols, background, background_reset,
- prefix, base_indent + number_width + 6, base_indent + number_width + 6,
- dim, dim_reset, name,
- matches, &emitted, visible, index == cursor, caret, &cursor_row, &cursor_column);
- }
+ line.cols = cols;
+ line.background = background;
+ line.background_reset = background_reset;
+ line.search = matches;
+ line.is_cursor = index == cursor;
+ line.caret = index == cursor ? caret_cols(line.text, item->text, edit_pos) : 0;
+ length = emit_wrapped(buffer, length, &line, &emitted, visible, &cursor_row, &cursor_column);
index++;
}
@@ -1122,6 +1090,10 @@ static int render(const struct List* list, int cursor, int scroll, enum Mode mod
"\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 if (editor->message[0] != '\0')
+ length += snprintf(buffer + length, sizeof buffer - (size_t) length,
+ "\x1b[%d;1H%*s\x1b[2m%s\x1b[0m\x1b[K",
+ rows, LEFT_MARGIN, "", editor->message);
else
length += snprintf(buffer + length, sizeof buffer - (size_t) length,
"\x1b[%d;1H%*s\x1b[2m[%s] %d/%d done\x1b[0m\x1b[K",
@@ -1221,53 +1193,84 @@ static int read_key(void)
return 27;
}
-static const char* item_display(const struct List* list, int index, int number_width,
- int* first_col, int* cont_col)
+static const char* strip_marker(const char* text)
{
- const struct Item* item = &list->items[index];
- int heading_level = 0;
+ const char* name = text + 1;
+
+ while (*name == ' ')
+ name++;
+
+ return name;
+}
+static int base_indent_of(const struct List* list, int index)
+{
for (int k = index - 1; k >= 0; k--)
if (is_section(&list->items[k]))
- {
- heading_level = section_level(&list->items[k]);
- break;
- }
+ return LEFT_MARGIN + section_level(&list->items[k]) * SECTION_INDENT;
- int base_indent = LEFT_MARGIN + heading_level * SECTION_INDENT;
+ return LEFT_MARGIN;
+}
+
+/* fill a Line's geometry, prefix string and style for a non-section item.
+ raw keeps the item's marker visible (used while editing it). the caller
+ fills the per-frame fields: cols, background, search, is_cursor, caret. */
+static void build_line(const struct List* list, int index, int number_width, int raw,
+ char* prefix, size_t prefix_size, struct Line* line)
+{
+ const struct Item* item = &list->items[index];
+ int base = base_indent_of(list, index);
+
+ line->style = "";
+ line->style_reset = "";
+ line->prefix = prefix;
if (is_note(item))
{
- int indent = base_indent + number_width + 6;
- *first_col = indent;
- *cont_col = indent + 2;
- return item->text;
+ int indent = base + number_width + 6;
+ line->text = item->text;
+ line->first_col = indent;
+ line->prefix_cols = indent + 2;
+ line->style = "\x1b[2;3m";
+ line->style_reset = "\x1b[22;23m";
+ (void) snprintf(prefix, prefix_size, "%*s", indent, "");
+ return;
}
- if (is_subtask(item))
- {
- int indent = base_indent + number_width + SUBTASK_INDENT;
- *first_col = *cont_col = indent + 4;
- const char* name = item->text + 1;
+ const char* box = item->state == STATE_DONE ? "[x]"
+ : item->state == STATE_DOING ? "[-]"
+ : "[ ]";
- while (*name == ' ')
- name++;
+ if (item->state == STATE_DONE)
+ {
+ line->style = "\x1b[2m";
+ line->style_reset = "\x1b[22m";
+ }
- return name;
+ if (is_subtask(item))
+ {
+ int indent = base + number_width + SUBTASK_INDENT;
+ line->text = raw ? item->text : strip_marker(item->text);
+ line->first_col = line->prefix_cols = indent + 4;
+ (void) snprintf(prefix, prefix_size, "%*s%s ", indent, "", box);
+ return;
}
- *first_col = *cont_col = base_indent + number_width + 6;
- const char* name = item->text;
+ int number = 0;
- if (is_optional(item))
- {
- name = item->text + 1;
+ for (int k = index; k >= 0 && !is_section(&list->items[k]); k--)
+ if (!is_child(&list->items[k]))
+ number++;
- while (*name == ' ')
- name++;
- }
+ const char* task_box = is_optional(item) ? (item->state == STATE_DONE ? "(x)"
+ : item->state == STATE_DOING ? "(-)"
+ : "( )")
+ : box;
- return name;
+ line->text = is_optional(item) && !raw ? strip_marker(item->text) : item->text;
+ line->first_col = line->prefix_cols = base + number_width + 6;
+ (void) snprintf(prefix, prefix_size, "%*s\x1b[2m%*d.\x1b[22m %s ",
+ base, "", number_width, number, task_box);
}
static int wrap_lines(const char* name, int cols, int first_col, int cont_col,
@@ -1374,9 +1377,12 @@ static void place_on_line(const struct List* list, int index, int number_width,
return;
}
- int first_col;
- int cont_col;
- const char* name = item_display(list, index, number_width, &first_col, &cont_col);
+ char prefix[48];
+ struct Line descriptor;
+ build_line(list, index, number_width, 0, prefix, sizeof prefix, &descriptor);
+ const char* name = descriptor.text;
+ int first_col = descriptor.first_col;
+ int cont_col = descriptor.prefix_cols;
int name_offset = (int) (name - list->items[index].text);
int starts[MAX_TEXT];
@@ -1392,8 +1398,11 @@ static void place_on_line(const struct List* list, int index, int number_width,
*edit_pos = name_offset + offset_at_width(name, starts[line], ends[line], want);
}
-static void move_visual(struct List* list, int* cursor, int* edit_pos, int number_width, int cols, int down)
+static void move_visual(struct List* list, struct Editor* editor, int number_width, int cols, int down)
{
+ int* cursor = &editor->cursor;
+ int* edit_pos = &editor->edit_pos;
+
if (*cursor < 0 || *cursor >= list->count)
return;
@@ -1404,9 +1413,12 @@ static void move_visual(struct List* list, int* cursor, int* edit_pos, int numbe
if (!is_section(item))
{
- int first_col;
- int cont_col;
- const char* name = item_display(list, *cursor, number_width, &first_col, &cont_col);
+ char prefix[48];
+ struct Line descriptor;
+ build_line(list, *cursor, number_width, 0, prefix, sizeof prefix, &descriptor);
+ const char* name = descriptor.text;
+ int first_col = descriptor.first_col;
+ int cont_col = descriptor.prefix_cols;
int name_offset = (int) (name - item->text);
int name_length = (int) strlen(name);
@@ -1414,20 +1426,20 @@ static void move_visual(struct List* list, int* cursor, int* edit_pos, int numbe
int ends[MAX_TEXT];
lines = wrap_lines(name, cols, first_col, cont_col, starts, ends, MAX_TEXT);
- int pos = *edit_pos - name_offset;
+ int position = *edit_pos - name_offset;
- if (pos < 0)
- pos = 0;
+ if (position < 0)
+ position = 0;
- if (pos > name_length)
- pos = name_length;
+ if (position > name_length)
+ position = name_length;
for (int i = 0; i < lines; i++)
- if (starts[i] <= pos)
+ if (starts[i] <= position)
line = i;
int base = line == 0 ? first_col : cont_col;
- goal = base + display_width(name, starts[line], pos);
+ goal = base + display_width(name, starts[line], position);
if (down && line < lines - 1)
{
@@ -1492,9 +1504,9 @@ static int matching_open(int close)
return 0;
}
-static void apply_change(struct List* list, int cursor, enum Mode* mode, int* edit_pos, int from, int to)
+static void apply_change(struct List* list, struct Editor* editor, int from, int to)
{
- struct Item* item = &list->items[cursor];
+ struct Item* item = &list->items[editor->cursor];
int length = (int) strlen(item->text);
if (to > length)
@@ -1505,58 +1517,263 @@ static void apply_change(struct List* list, int cursor, enum Mode* mode, int* ed
history_record(list);
memmove(&item->text[from], &item->text[to], (size_t) (length - to) + 1);
- *edit_pos = from;
- *mode = MODE_INSERT;
+ editor->edit_pos = from;
+ editor->mode = MODE_INSERT;
+}
+
+static void delete_text(struct List* list, struct Editor* editor, int from, int to)
+{
+ struct Item* item = &list->items[editor->cursor];
+ int length = (int) strlen(item->text);
+
+ if (to > length)
+ to = length;
+
+ if (from < 0 || to <= from)
+ return;
+
+ history_record(list);
+ memmove(&item->text[from], &item->text[to], (size_t) (length - to) + 1);
+ editor->edit_pos = from;
+}
+
+static int motion_range(struct List* list, const struct Editor* editor, int op, int* from, int* to)
+{
+ int cursor = editor->cursor;
+ int edit_pos = editor->edit_pos;
+ int count = editor->count > 0 ? editor->count : 1;
+ const char* text = list->items[cursor].text;
+ int length = (int) strlen(text);
+ int low = text_start(&list->items[cursor]);
+
+ *from = edit_pos;
+ *to = edit_pos;
+
+ if (op == 'w')
+ {
+ int position = edit_pos;
+
+ for (int r = 0; r < count; r++)
+ {
+ while (position < length && text[position] != ' ')
+ position++;
+
+ while (position < length && text[position] == ' ')
+ position++;
+ }
+
+ *to = position;
+ return 1;
+ }
+
+ if (op == 'e')
+ {
+ int position = edit_pos;
+
+ for (int r = 0; r < count; r++)
+ {
+ while (position < length && text[position] == ' ')
+ position++;
+
+ while (position < length && text[position] != ' ')
+ position++;
+ }
+
+ *to = position;
+ return 1;
+ }
+
+ if (op == 'b')
+ {
+ int position = edit_pos;
+
+ for (int r = 0; r < count; r++)
+ {
+ if (position > low)
+ position--;
+
+ while (position > low && text[position] == ' ')
+ position--;
+
+ while (position > low && text[position - 1] != ' ')
+ position--;
+ }
+
+ *from = position;
+ return 1;
+ }
+
+ if (op == '$')
+ {
+ *to = length;
+ return 1;
+ }
+
+ if (op == '0')
+ {
+ *from = low;
+ return 1;
+ }
+
+ if (op == 'l')
+ {
+ for (int r = 0; r < count; r++)
+ *to = utf8_right(text, *to);
+
+ return 1;
+ }
+
+ if (op == 'h')
+ {
+ for (int r = 0; r < count; r++)
+ *from = utf8_left(text, *from);
+
+ return 1;
+ }
+
+ if (op != 't' && op != 'f' && op != 'i' && op != 'a')
+ {
+ unget_key(op);
+ return 0;
+ }
+
+ int target = read_key();
+
+ if (target < 32 || target >= 256)
+ return 0;
+
+ if (op == 't' || op == 'f')
+ {
+ int found = -1;
+
+ for (int i = edit_pos + 1; i < length; i++)
+ if ((unsigned char) text[i] == target)
+ {
+ found = i;
+ break;
+ }
+
+ if (found < 0)
+ return 0;
+
+ *from = edit_pos;
+ *to = op == 'f' ? found + 1 : found;
+ return 1;
+ }
+
+ int open_char = target;
+ int close_char = matching_close(target);
+
+ if (close_char == 0)
+ {
+ int open = matching_open(target);
+
+ if (open != 0)
+ open_char = open;
+
+ close_char = target;
+ }
+
+ int open_at = -1;
+ int close_at = -1;
+
+ for (int i = edit_pos; i >= low; i--)
+ if ((unsigned char) text[i] == open_char)
+ {
+ open_at = i;
+ break;
+ }
+
+ if (open_at >= 0)
+ {
+ for (int i = open_at + 1; i < length; i++)
+ if ((unsigned char) text[i] == close_char)
+ {
+ close_at = i;
+ break;
+ }
+
+ if (close_at < edit_pos)
+ {
+ open_at = -1;
+ close_at = -1;
+ }
+ }
+
+ if (open_at < 0)
+ {
+ for (int i = edit_pos; i < length; i++)
+ if ((unsigned char) text[i] == open_char)
+ {
+ open_at = i;
+ break;
+ }
+
+ for (int i = open_at + 1; open_at >= 0 && i < length; i++)
+ if ((unsigned char) text[i] == close_char)
+ {
+ close_at = i;
+ break;
+ }
+ }
+
+ if (open_at < 0 || close_at < 0)
+ return 0;
+
+ if (op == 'i')
+ {
+ *from = open_at + 1;
+ *to = close_at;
+ }
+ else
+ {
+ *from = open_at;
+ *to = close_at + 1;
+ }
+
+ return 1;
}
-static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit_pos,
- char* command, char* search, int count, int key)
+static int normal_key(struct List* list, struct Editor* editor, int key)
{
+ int* cursor = &editor->cursor;
+ int* edit_pos = &editor->edit_pos;
+ enum Mode* mode = &editor->mode;
+ char* command = editor->command;
+ char* search = editor->search;
+ int count = editor->count > 0 ? editor->count : 1;
+
switch (key)
{
case 27:
+ {
search[0] = '\0';
break;
+ }
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;
- int off = match_offset(list->items[match].text, search);
-
- if (off >= 0)
- *edit_pos = off;
- }
-
+ goto_match(list, editor, 1);
break;
}
case 'N':
{
- int match = find_match(list, *cursor, -1, search);
-
- if (match >= 0)
- {
- *cursor = match;
- int off = match_offset(list->items[match].text, search);
-
- if (off >= 0)
- *edit_pos = off;
- }
-
+ goto_match(list, editor, -1);
break;
}
@@ -1585,12 +1802,16 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
case 'p':
- paste_item(list, item_block_end(list, *cursor), cursor);
+ {
+ paste_item(list, editor, item_block_end(list, *cursor));
break;
+ }
case 'P':
- paste_item(list, *cursor, cursor);
+ {
+ paste_item(list, editor, *cursor);
break;
+ }
case 'j':
case KEY_DOWN:
@@ -1599,7 +1820,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
int cols = wrap_column();
for (int repeat = 0; repeat < count; repeat++)
- move_visual(list, cursor, edit_pos, number_width, cols, 1);
+ move_visual(list, editor, number_width, cols, 1);
break;
}
@@ -1611,40 +1832,49 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
int cols = wrap_column();
for (int repeat = 0; repeat < count; repeat++)
- move_visual(list, cursor, edit_pos, number_width, cols, 0);
+ move_visual(list, editor, number_width, cols, 0);
break;
}
case 'h':
case KEY_LEFT:
+ {
if (*cursor >= 0 && *cursor < list->count)
for (int repeat = 0; repeat < count; repeat++)
*edit_pos = utf8_left(list->items[*cursor].text, *edit_pos);
break;
+ }
case 'l':
case KEY_RIGHT:
+ {
if (*cursor >= 0 && *cursor < list->count)
for (int repeat = 0; repeat < count; repeat++)
*edit_pos = utf8_right(list->items[*cursor].text, *edit_pos);
break;
+ }
case '0':
+ {
if (*cursor >= 0 && *cursor < list->count)
*edit_pos = text_start(&list->items[*cursor]);
break;
+ }
case '$':
+ {
if (*cursor >= 0 && *cursor < list->count)
*edit_pos = (int) strlen(list->items[*cursor].text);
break;
+ }
case 'w':
+ {
if (*cursor >= 0 && *cursor < list->count)
{
const char* text = list->items[*cursor].text;
@@ -1664,8 +1894,10 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'b':
+ {
if (*cursor >= 0 && *cursor < list->count)
{
const char* text = list->items[*cursor].text;
@@ -1687,17 +1919,26 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'u':
+ {
history_undo(list);
break;
+ }
case 18:
+ {
history_redo(list);
break;
+ }
case ' ':
- if (*cursor >= 0 && *cursor < list->count && !is_section(&list->items[*cursor]) && !is_note(&list->items[*cursor]))
+ {
+ if (*cursor >= 0 &&
+ *cursor < list->count &&
+ !is_section(&list->items[*cursor]) &&
+ !is_note(&list->items[*cursor]))
{
history_record(list);
struct Item* item = &list->items[*cursor];
@@ -1706,8 +1947,10 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'x':
+ {
if (*cursor >= 0 && *cursor < list->count
&& *edit_pos < (int) strlen(list->items[*cursor].text))
{
@@ -1724,6 +1967,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'o':
{
@@ -1738,6 +1982,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
case 'A':
+ {
history_record(list);
if (list->count == 0)
@@ -1753,8 +1998,10 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'i':
+ {
if (*cursor >= 0 && *cursor < list->count)
{
history_record(list);
@@ -1768,8 +2015,10 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'R':
+ {
history_record(list);
if (list->count == 0)
@@ -1787,6 +2036,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
break;
+ }
case 'c':
{
@@ -1795,174 +2045,22 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
int op = read_key();
- const char* text = list->items[*cursor].text;
- int length = (int) strlen(text);
- int low = text_start(&list->items[*cursor]);
-
- if (op == 'w' || op == 'e' || op == 'b' || op == '$'
- || op == '0' || op == 'c' || op == 'h' || op == 'l')
+ if (op == 'c')
{
- int from = *edit_pos;
- int to = *edit_pos;
-
- if (op == 'w' || op == 'e')
- {
- int pos = *edit_pos;
-
- for (int r = 0; r < count; r++)
- {
- while (pos < length && text[pos] == ' ')
- pos++;
-
- while (pos < length && text[pos] != ' ')
- pos++;
- }
-
- to = pos;
- }
- else if (op == 'b')
- {
- int pos = *edit_pos;
-
- for (int r = 0; r < count; r++)
- {
- if (pos > low)
- pos--;
-
- while (pos > low && text[pos] == ' ')
- pos--;
-
- while (pos > low && text[pos - 1] != ' ')
- pos--;
- }
-
- from = pos;
- }
- else if (op == '$')
- {
- to = length;
- }
- else if (op == '0')
- {
- from = low;
- }
- else if (op == 'c')
- {
- from = low;
- to = length;
- }
- else if (op == 'l')
- {
- for (int r = 0; r < count; r++)
- to = utf8_right(text, to);
- }
- else
- {
- for (int r = 0; r < count; r++)
- from = utf8_left(text, from);
- }
-
- apply_change(list, *cursor, mode, edit_pos, from, to);
+ const struct Item* item = &list->items[*cursor];
+ apply_change(list, editor,
+ text_start(item), (int) strlen(item->text));
break;
}
- if (op != 't' && op != 'f' && op != 'i' && op != 'a')
- {
- unget_key(op);
- break;
- }
-
- int target = read_key();
-
- if (target < 32 || target >= 256)
- break;
-
- if (op == 't' || op == 'f')
- {
- int found = -1;
-
- for (int i = *edit_pos + 1; i < length; i++)
- if ((unsigned char) text[i] == target)
- {
- found = i;
- break;
- }
-
- if (found >= 0)
- apply_change(list, *cursor, mode, edit_pos,
- *edit_pos, op == 'f' ? found + 1 : found);
-
- break;
- }
-
- int open_char = target;
- int close_char = matching_close(target);
-
- if (close_char == 0)
- {
- int open = matching_open(target);
-
- if (open != 0)
- {
- open_char = open;
- close_char = target;
- }
- else
- {
- close_char = target;
- }
- }
-
- int open_at = -1;
- int close_at = -1;
+ if (op == 'w')
+ op = 'e';
- for (int i = *edit_pos; i >= low; i--)
- if ((unsigned char) text[i] == open_char)
- {
- open_at = i;
- break;
- }
-
- if (open_at >= 0)
- {
- for (int i = open_at + 1; i < length; i++)
- if ((unsigned char) text[i] == close_char)
- {
- close_at = i;
- break;
- }
+ int from;
+ int to;
- if (close_at < *edit_pos)
- {
- open_at = -1;
- close_at = -1;
- }
- }
-
- if (open_at < 0)
- {
- for (int i = *edit_pos; i < length; i++)
- if ((unsigned char) text[i] == open_char)
- {
- open_at = i;
- break;
- }
-
- for (int i = open_at + 1; open_at >= 0 && i < length; i++)
- if ((unsigned char) text[i] == close_char)
- {
- close_at = i;
- break;
- }
- }
-
- if (open_at < 0 || close_at < 0)
- break;
-
- if (op == 'i')
- apply_change(list, *cursor, mode, edit_pos, open_at + 1, close_at);
- else
- apply_change(list, *cursor, mode, edit_pos, open_at, close_at + 1);
+ if (motion_range(list, editor, op, &from, &to))
+ apply_change(list, editor, from, to);
break;
}
@@ -1980,8 +2078,10 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
case 'G':
+ {
*cursor = list->count - 1;
break;
+ }
case '}':
{
@@ -2007,35 +2107,46 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
case 'd':
{
- int next = read_key();
+ int op = read_key();
- if (next != 'd')
+ if (op == 'd')
{
- unget_key(next);
- break;
- }
+ if (*cursor >= 0 && *cursor < list->count)
+ {
+ history_record(list);
- if (*cursor >= 0 && *cursor < list->count)
- {
- history_record(list);
+ int start = *cursor;
+ int end = start;
- int start = *cursor;
- int end = start;
+ for (int repeat = 0; repeat < count && end < list->count; repeat++)
+ end = item_block_end(list, end);
- for (int repeat = 0; repeat < count && end < list->count; repeat++)
- end = item_block_end(list, end);
+ yank_count = 0;
- yank_count = 0;
+ for (int i = start; i < end && yank_count < MAX_ITEMS; i++)
+ yank_register[yank_count++] = list->items[i];
- for (int i = start; i < end && yank_count < MAX_ITEMS; i++)
- yank_register[yank_count++] = list->items[i];
+ for (int i = start; i < end; i++)
+ delete_item(list, start);
- for (int i = start; i < end; i++)
- delete_item(list, start);
+ commit(list);
+ }
- commit(list);
+ break;
}
+ if (*cursor < 0 || *cursor >= list->count)
+ {
+ unget_key(op);
+ break;
+ }
+
+ int from;
+ int to;
+
+ if (motion_range(list, editor, op, &from, &to))
+ delete_text(list, editor, from, to);
+
break;
}
@@ -2058,7 +2169,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
*cursor = start + (next_end - end);
}
}
- else if (!is_subtask(&list->items[*cursor]) && !is_note(&list->items[*cursor]))
+ else if (!is_child(&list->items[*cursor]))
{
int start = *cursor;
int end = task_block_end(list, start);
@@ -2073,8 +2184,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
*cursor = start + (next_end - end);
}
}
- else if (*cursor + 1 < list->count
- && (is_subtask(&list->items[*cursor + 1]) || is_note(&list->items[*cursor + 1])))
+ else if (*cursor + 1 < list->count && is_child(&list->items[*cursor + 1]))
{
history_record(list);
struct Item moved = list->items[*cursor];
@@ -2109,13 +2219,12 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
*cursor = previous_start;
}
}
- else if (!is_subtask(&list->items[*cursor]) && !is_note(&list->items[*cursor]))
+ else if (!is_child(&list->items[*cursor]))
{
int start = *cursor;
int previous = start - 1;
- while (previous >= 0
- && (is_subtask(&list->items[previous]) || is_note(&list->items[previous])))
+ while (previous >= 0 && is_child(&list->items[previous]))
previous--;
if (previous >= 0 && !is_section(&list->items[previous]))
@@ -2128,8 +2237,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
*cursor = previous;
}
}
- else if (*cursor > 0
- && (is_subtask(&list->items[*cursor - 1]) || is_note(&list->items[*cursor - 1])))
+ else if (*cursor > 0 && is_child(&list->items[*cursor - 1]))
{
history_record(list);
struct Item moved = list->items[*cursor];
@@ -2150,9 +2258,7 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
{
int end = item_block_end(list, i);
- int drop = !is_section(&list->items[i])
- && !is_subtask(&list->items[i])
- && !is_note(&list->items[i])
+ int drop = is_task(&list->items[i])
&& list->items[i].state == STATE_DONE;
if (!drop)
@@ -2168,14 +2274,19 @@ static int normal_key(struct List* list, int* cursor, enum Mode* mode, int* edit
}
default:
+ {
break;
+ }
}
return 1;
}
-static void insert_key(struct List* list, int* cursor, enum Mode* mode, int* edit_pos, int key)
+static void insert_key(struct List* list, struct Editor* editor, int key)
{
+ int* cursor = &editor->cursor;
+ int* edit_pos = &editor->edit_pos;
+ enum Mode* mode = &editor->mode;
struct Item* item = &list->items[*cursor];
size_t length = strlen(item->text);
@@ -2263,8 +2374,10 @@ static void insert_key(struct List* list, int* cursor, enum Mode* mode, int* edi
}
}
-static int command_key(struct List* list, const char* path, enum Mode* mode, char* command, int key)
+static int command_key(struct List* list, const char* path, struct Editor* editor, int key)
{
+ enum Mode* mode = &editor->mode;
+ char* command = editor->command;
size_t length = strlen(command);
if (key == 27)
@@ -2276,7 +2389,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)
+ if (command[0] == '\0')
+ {
+ *mode = MODE_NORMAL;
+ return 1;
+ }
+
+ if (strcmp(command, "reset") == 0 ||
+ strcmp(command, "clear") == 0)
{
pending_action = strcmp(command, "clear") == 0 ? PENDING_CLEAR : PENDING_RESET;
command[0] = '\0';
@@ -2293,8 +2413,20 @@ static int command_key(struct List* list, const char* path, enum Mode* mode, cha
return 1;
}
- int write = strchr(command, 'w') != NULL || strcmp(command, "x") == 0;
- int quit = strchr(command, 'q') != NULL || strcmp(command, "x") == 0;
+ int write = strcmp(command, "w") == 0 ||
+ strcmp(command, "wq") == 0 ||
+ strcmp(command, "x") == 0;
+ int quit = strcmp(command, "q") == 0 ||
+ strcmp(command, "wq") == 0 ||
+ strcmp(command, "x") == 0;
+
+ if (!write && !quit)
+ {
+ (void) snprintf(editor->message, sizeof editor->message, "not a command: %s", command);
+ command[0] = '\0';
+ *mode = MODE_NORMAL;
+ return 1;
+ }
*mode = MODE_NORMAL;
@@ -2328,8 +2460,11 @@ 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, int* edit_pos, enum Mode* mode, char* command, char* search, int key)
+static void search_key(const struct List* list, struct Editor* editor, int key)
{
+ enum Mode* mode = &editor->mode;
+ char* command = editor->command;
+ char* search = editor->search;
size_t length = strlen(command);
if (key == 27)
@@ -2344,18 +2479,7 @@ static void search_key(const struct List* list, int* cursor, int* edit_pos, enum
(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;
- int off = match_offset(list->items[match].text, search);
-
- if (off >= 0)
- *edit_pos = off;
- }
-
+ goto_match(list, editor, 1);
return;
}
@@ -2376,8 +2500,10 @@ static void search_key(const struct List* list, int* cursor, int* edit_pos, enum
}
}
-static void confirm_key(struct List* list, enum Mode* mode, int key)
+static void confirm_key(struct List* list, struct Editor* editor, int key)
{
+ enum Mode* mode = &editor->mode;
+
if (key == 'y' || key == 'Y')
{
history_record(list);
@@ -2408,91 +2534,97 @@ static void run(struct List* list, const char* path)
normalize_tasks(list);
sort_list(list);
- enum Mode mode = MODE_NORMAL;
- int cursor = 0;
- int scroll = 0;
- int edit_pos = 0;
- int count = 0;
- char command[MAX_COMMAND] = "";
- char search[MAX_COMMAND] = "";
+ struct Editor editor = { 0 };
+ editor.mode = MODE_NORMAL;
for (;;)
{
- if (cursor >= list->count)
- cursor = list->count - 1;
+ if (editor.cursor >= list->count)
+ editor.cursor = list->count - 1;
- if (cursor < 0)
- cursor = 0;
+ if (editor.cursor < 0)
+ editor.cursor = 0;
if (list->count > 0)
{
- const char* text = list->items[cursor].text;
+ const char* text = list->items[editor.cursor].text;
int text_length = (int) strlen(text);
- int caret_max = is_editing(mode) ? text_length : (text_length > 0 ? text_length - 1 : 0);
- int caret_min = is_editing(mode) ? 0 : text_start(&list->items[cursor]);
+ int caret_max = is_editing(editor.mode) ? text_length : (text_length > 0 ? text_length - 1 : 0);
+ int caret_min = is_editing(editor.mode) ? 0 : text_start(&list->items[editor.cursor]);
if (caret_min > caret_max)
caret_min = caret_max;
- if (edit_pos > caret_max)
- edit_pos = caret_max;
+ if (editor.edit_pos > caret_max)
+ editor.edit_pos = caret_max;
- if (edit_pos < caret_min)
- edit_pos = caret_min;
+ if (editor.edit_pos < caret_min)
+ editor.edit_pos = caret_min;
- while (edit_pos > caret_min && ((unsigned char) text[edit_pos] & 0xC0) == 0x80)
- edit_pos--;
+ while (editor.edit_pos > caret_min && ((unsigned char) text[editor.edit_pos] & 0xC0) == 0x80)
+ editor.edit_pos--;
}
- if (cursor < scroll)
- scroll = cursor;
+ if (editor.cursor < editor.scroll)
+ editor.scroll = editor.cursor;
- while (scroll < cursor
- && render(list, cursor, scroll, mode, edit_pos, command, search, 1) < 0)
- scroll++;
+ while (editor.scroll < editor.cursor && render(list, &editor, 1) < 0)
+ editor.scroll++;
- render(list, cursor, scroll, mode, edit_pos, command, search, 0);
+ render(list, &editor, 0);
int key = read_key();
if (key == -1)
break;
- switch (mode)
+ editor.message[0] = '\0';
+
+ switch (editor.mode)
{
case MODE_NORMAL:
- if ((key >= '1' && key <= '9') || (key == '0' && count > 0))
+ {
+ if ((key >= '1' && key <= '9') || (key == '0' && editor.count > 0))
{
- count = count * 10 + (key - '0');
+ editor.count = editor.count * 10 + (key - '0');
- if (count > 9999)
- count = 9999;
+ if (editor.count > 9999)
+ editor.count = 9999;
break;
}
- normal_key(list, &cursor, &mode, &edit_pos, command, search, count > 0 ? count : 1, key);
- count = 0;
+ normal_key(list, &editor, key);
+ editor.count = 0;
break;
+ }
case MODE_INSERT:
case MODE_REPLACE:
- insert_key(list, &cursor, &mode, &edit_pos, key);
+ {
+ insert_key(list, &editor, key);
break;
+ }
case MODE_COMMAND:
- if (command_key(list, path, &mode, command, key) == 0)
+ {
+ if (command_key(list, path, &editor, key) == 0)
return;
break;
+ }
case MODE_SEARCH:
- search_key(list, &cursor, &edit_pos, &mode, command, search, key);
+ {
+ search_key(list, &editor, key);
break;
+ }
case MODE_CONFIRM:
- confirm_key(list, &mode, key);
+ {
+ confirm_key(list, &editor, key);
break;
+ }
}
}
}
@@ -2518,6 +2650,7 @@ static void print_help(void)
" R overtype (replace) 3j 2dd counts\n"
" x delete char under cursor space cycle state\n"
" cw ce cb c0 c$ cc change word / to start / end / whole\n"
+ " dw de db d0 d$ delete over a motion (also c/d t f i a)\n"
" ct{c} cf{c} change till / onto a char\n"
" ci{d} ca{d} change inside / around () [] {} <> \" '\n"
" dd cut item D clear done yy p P yank/paste\n"