URI: 
       tStart implementing more complex commands - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 174e6af4e28e5dec7b1402de9b15174357898865
   DIR parent 593315dc549190dd7ed38194fce885cae741c216
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat, 24 Apr 2021 19:28:21 +0200
       
       Start implementing more complex commands
       
       Diffstat:
         M buffer.c                            |       2 ++
         M buffer.h                            |       2 ++
         M ledit.c                             |     287 +++++++++++++++++++++++++------
       
       3 files changed, 240 insertions(+), 51 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -31,6 +31,8 @@ ledit_create_buffer(ledit_common_state *state) {
                buffer->cur_line = 0;
                buffer->cur_index = 0;
                buffer->trailing = 0;
       +        buffer->trailing_bytes = 0;
       +        buffer->end_of_soft_line = 0;
                buffer->total_height = 0;
                buffer->display_offset = 0;
                ledit_append_line(buffer, -1, -1);
   DIR diff --git a/buffer.h b/buffer.h
       t@@ -22,6 +22,8 @@ struct ledit_buffer {
                int cur_index; /* current byte index in line */
                int trailing; /* used by pango for determining if index is at
                               * beginning or end of character */
       +        int trailing_bytes; /* same thing, but with bytes instead of utf8 characters */
       +        int end_of_soft_line; /* used to handle special behavior at end end of soft line */
                long total_height; /* total pixel height of all lines */
                double display_offset; /* current pixel offset of viewport - this
                                        * is a double to make scrolling smoother */
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -1,3 +1,4 @@
       +/* FIXME: Use PANGO_PIXELS() */
        /* FIXME: Fix cursor movement, especially buffer->trailing and writing at end of line */
        /* FIXME: horizontal scrolling (also need cache to avoid too large pixmaps) */
        /* FIXME: sort out types for indices (currently just int, but that might overflow) */
       t@@ -26,16 +27,52 @@
        #include "buffer.h"
        #include "cache.h"
        
       +enum key_type {
       +        KEY_NONE = 0,
       +        KEY_MISC = 1,
       +        KEY_CHAR = 2,
       +        KEY_MOTION = 4,
       +        KEY_NUMBER = 8,
       +        KEY_NUMBERALLOWED = 16,
       +        KEY_ANY = 0xFF
       +};
       +
        struct key {
                char *text;            /* for keys that correspond with text */
                KeySym keysym;         /* for other keys, e.g. arrow keys */
                enum ledit_mode modes; /* modes in which this keybinding is functional */
       +        enum key_type key_types; /* key types - used to determine if the key is allowed */
                void (*func)(void);    /* callback function */
        };
        
       +struct key_stack_elem {
       +        enum key_type key; /* key type */
       +        enum key_type followup; /* allowed keys to complete the keybinding */
       +        void (*func)(void); /* function to call if an allowed key is entered */
       +        int data1; /* misc. data 1 */
       +        int data2; /* misc. data 2 */
       +};
       +
       +static struct {
       +        size_t len, alloc;
       +        struct key_stack_elem *stack;
       +} key_stack;
       +
        static ledit_common_state state;
        static ledit_buffer *buffer;
        
       +/* TODO: protect against overflow, especially on repeating commands */
       +
       +static struct key_stack_elem *push_key_stack(
       +    enum key_type key,
       +    enum key_type followup,
       +    void (*func)(void),
       +    int data1, int data2
       +);
       +static struct key_stack_elem *peek_key_stack(void);
       +static struct key_stack_elem *pop_key_stack(void);
       +void clear_key_stack(void);
       +
        static void set_scroll_pos(double pos);
        static void get_scroll_pos_height(double *pos, double *height);
        static void resize_window(int w, int h);
       t@@ -54,16 +91,103 @@ static void delete_key(void);
        static void move_cursor(int dir);
        static void cursor_left(void);
        static void cursor_right(void);
       +static void cursor_down(void);
       +static void cursor_up(void);
       +static void cursor_to_beginning(void);
        static void return_key(void);
        static void escape_key(void);
       -static void i_key(void);
       -static void line_down(void);
       -static void line_up(void);
       -static void zero_key(void);
       +static void enter_insert(void);
       +static void key_x(void);
       +static void push_num(int num);
       +static void push_0(void);
       +static void push_1(void);
       +static void push_2(void);
       +static void push_3(void);
       +static void push_4(void);
       +static void push_5(void);
       +static void push_6(void);
       +static void push_7(void);
       +static void push_8(void);
       +static void push_9(void);
        
        static void change_keyboard(char *lang);
        static void key_press(XEvent event);
        
       +static void
       +key_x(void) {
       +        struct key_stack_elem *e = pop_key_stack();
       +        int num = 1;
       +        clear_key_stack();
       +        if (e && !(e->key & KEY_NUMBER))
       +                return;
       +        if (e)
       +                num = e->data1;
       +        if (num <= 0)
       +                num = 1;
       +        printf("delete %d\n", num);
       +}
       +
       +static void
       +push_num(int num) {
       +        struct key_stack_elem *e = peek_key_stack();
       +        if (!e || !(e->key & KEY_NUMBER))
       +                e = push_key_stack(KEY_NUMBER, KEY_NUMBERALLOWED, NULL, 0, 0);
       +        /* FIXME: error checking */
       +        e->data1 *= 10;
       +        e->data1 += num;
       +}
       +
       +/* FIXME: CHANGE BEHAVIOR TO MOVEMENT */
       +static void
       +push_0(void) {
       +        push_num(0);
       +}
       +
       +static void
       +push_1(void) {
       +        push_num(1);
       +}
       +
       +static void
       +push_2(void) {
       +        push_num(2);
       +}
       +
       +static void
       +push_3(void) {
       +        push_num(3);
       +}
       +
       +static void
       +push_4(void) {
       +        push_num(4);
       +}
       +
       +static void
       +push_5(void) {
       +        push_num(5);
       +}
       +
       +static void
       +push_6(void) {
       +        push_num(6);
       +}
       +
       +static void
       +push_7(void) {
       +        push_num(7);
       +}
       +
       +static void
       +push_8(void) {
       +        push_num(8);
       +}
       +
       +static void
       +push_9(void) {
       +        push_num(9);
       +}
       +
        int
        main(int argc, char *argv[]) {
                setup(argc, argv);
       t@@ -73,6 +197,53 @@ main(int argc, char *argv[]) {
                return 0;
        }
        
       +static struct key_stack_elem *
       +push_key_stack(
       +    enum key_type key,
       +    enum key_type followup,
       +    void (*func)(void),
       +    int data1, int data2) {
       +        struct key_stack_elem *e;
       +        if (key_stack.len >= key_stack.alloc) {
       +                size_t new_alloc = key_stack.alloc > 0 ? key_stack.alloc * 2 : 4;
       +                key_stack.stack = ledit_realloc(
       +                    key_stack.stack, new_alloc * sizeof(struct key_stack_elem)
       +                );
       +                key_stack.alloc = new_alloc;
       +        }
       +        e = &key_stack.stack[key_stack.len];
       +        e->key = key;
       +        e->followup = followup;
       +        e->func = func;
       +        e->data1 = data1;
       +        e->data2 = data2;
       +        key_stack.len++;
       +        return &key_stack.stack[key_stack.len - 1];
       +}
       +
       +/* Note: for peek and pop, the returned element is only valid
       + * until the next element is pushed */
       +static struct key_stack_elem *
       +peek_key_stack(void) {
       +        if (key_stack.len > 0)
       +                return &key_stack.stack[key_stack.len - 1];
       +        return NULL;
       +}
       +
       +static struct key_stack_elem *
       +pop_key_stack(void) {
       +        if (key_stack.len > 0) {
       +                key_stack.len--;
       +                return &key_stack.stack[key_stack.len];
       +        }
       +        return NULL;
       +}
       +
       +void
       +clear_key_stack(void) {
       +        key_stack.len = 0;
       +}
       +
        static void
        get_scroll_pos_height(double *pos, double *height) {
                *height = ((double)state.h / buffer->total_height) * state.h;
       t@@ -316,6 +487,10 @@ setup(int argc, char *argv[]) {
        
                ledit_init_cache(&state);
                buffer = ledit_create_buffer(&state);
       +
       +        key_stack.len = key_stack.alloc = 0;
       +        key_stack.stack = NULL;
       +
                redraw();
        }
        
       t@@ -572,17 +747,23 @@ delete_key(void) {
                ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
        }
        
       +/* FIXME: this comment needs to be implemented still */
       +/* num is number of graphemes to move (see pango documentation) - negative
       + * is left, positive is right */
        static void
        move_cursor(int dir) {
       -        int last_index = buffer->cur_index;
       +        /* FIXME: trailing */
       +        int trailing, last_index = buffer->cur_index;
                ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
                pango_layout_move_cursor_visually(
                    cur_line->layout, TRUE,
                    buffer->cur_index, buffer->trailing, dir,
                    &buffer->cur_index, &buffer->trailing
                );
       +        /* FIXME: Allow cursor to be at end of soft line */
                /* we don't currently support a difference between the cursor being at
                   the end of a soft line and the beginning of the next line */
       +        /*
                while (buffer->trailing > 0) {
                        buffer->trailing--;
                        buffer->cur_index++;
       t@@ -590,6 +771,7 @@ move_cursor(int dir) {
                               ((cur_line->text[buffer->cur_index] & 0xC0) == 0x80))
                                buffer->cur_index++;
                }
       +        */
                if (buffer->cur_index < 0)
                        buffer->cur_index = 0;
                /* when in normal mode, the cursor cannot be at the very end
       t@@ -643,13 +825,13 @@ escape_key(void) {
        }
        
        static void
       -i_key(void) {
       +enter_insert(void) {
                state.mode = INSERT;
                ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
        }
        
        static void
       -line_down(void) {
       +cursor_down(void) {
                int lineno, x, trailing = 0;
                ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
                pango_layout_index_to_line_x(
       t@@ -706,7 +888,7 @@ line_down(void) {
        }
        
        static void
       -line_up(void) {
       +cursor_up(void) {
                int lineno, x, trailing = 0;
                ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
                pango_layout_index_to_line_x(
       t@@ -767,60 +949,63 @@ line_up(void) {
        }
        
        static void
       -zero_key(void) {
       +cursor_to_beginning(void) {
                buffer->cur_index = 0;
                ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
        }
        
        static struct key keys_en[] = {
       -        {NULL, XK_BackSpace, INSERT, &backspace},
       -        {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
       -        {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
       -        {NULL, XK_Up, INSERT|NORMAL, &line_up},
       -        {NULL, XK_Down, INSERT|NORMAL, &line_down},
       -        {NULL, XK_Return, INSERT, &return_key},
       -        {NULL, XK_Delete, INSERT, &delete_key},
       -        {NULL, XK_Escape, INSERT, &escape_key},
       -        {"i",  0, NORMAL, &i_key},
       -        {"h",  0, NORMAL, &cursor_left},
       -        {"l",  0, NORMAL, &cursor_right},
       -        {"j",  0, NORMAL, &line_down},
       -        {"k",  0, NORMAL, &line_up},
       -        {"0",  0, NORMAL, &zero_key}
       +        {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace},
       +        {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left},
       +        {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right},
       +        {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up},
       +        {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down},
       +        {NULL, XK_Return, INSERT, KEY_ANY, &return_key},
       +        {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key},
       +        {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key},
       +        {"i",  0, NORMAL, KEY_ANY, &enter_insert},
       +        {"h",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       +        {"l",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       +        {"j",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       +        {"k",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       +        {"0",  0, NORMAL, KEY_ANY, &cursor_to_beginning},
       +        {"1",  0, NORMAL, KEY_NUMBER, &push_1},
       +        {"2",  0, NORMAL, KEY_NUMBER, &push_2},
       +        {"x",  0, NORMAL, KEY_NUMBERALLOWED, &key_x}
        };
        
        static struct key keys_ur[] = {
       -        {NULL, XK_BackSpace, INSERT, &backspace},
       -        {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
       -        {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
       -        {NULL, XK_Up, INSERT|NORMAL, &line_up},
       -        {NULL, XK_Down, INSERT|NORMAL, &line_down},
       -        {NULL, XK_Return, INSERT, &return_key},
       -        {NULL, XK_Delete, INSERT, &delete_key},
       -        {NULL, XK_Escape, INSERT, &escape_key},
       -        {"ی",  0, NORMAL, &i_key},
       -        {"ح",  0, NORMAL, &cursor_left},
       -        {"ل",  0, NORMAL, &cursor_right},
       -        {"ج",  0, NORMAL, &line_down},
       -        {"ک",  0, NORMAL, &line_up},
       -        {"0",  0, NORMAL, &zero_key}
       +        {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace},
       +        {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left},
       +        {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right},
       +        {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up},
       +        {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down},
       +        {NULL, XK_Return, INSERT, KEY_ANY, &return_key},
       +        {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key},
       +        {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key},
       +        {"ی",  0, NORMAL, KEY_ANY, &enter_insert},
       +        {"ح",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       +        {"ل",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       +        {"ج",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       +        {"ک",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       +        {"0",  0, NORMAL, KEY_ANY, &cursor_to_beginning}
        };
        
        static struct key keys_hi[] = {
       -        {NULL, XK_BackSpace, INSERT, &backspace},
       -        {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
       -        {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
       -        {NULL, XK_Up, INSERT|NORMAL, &line_up},
       -        {NULL, XK_Down, INSERT|NORMAL, &line_down},
       -        {NULL, XK_Return, INSERT, &return_key},
       -        {NULL, XK_Delete, INSERT, &delete_key},
       -        {NULL, XK_Escape, INSERT, &escape_key},
       -        {"ि",  0, NORMAL, &i_key},
       -        {"ह",  0, NORMAL, &cursor_left},
       -        {"ल",  0, NORMAL, &cursor_right},
       -        {"ज",  0, NORMAL, &line_down},
       -        {"क",  0, NORMAL, &line_up},
       -        {"0",  0, NORMAL, &zero_key}
       +        {NULL, XK_BackSpace, INSERT, KEY_ANY, &backspace},
       +        {NULL, XK_Left, INSERT|NORMAL, KEY_ANY, &cursor_left},
       +        {NULL, XK_Right, INSERT|NORMAL, KEY_ANY, &cursor_right},
       +        {NULL, XK_Up, INSERT|NORMAL, KEY_ANY, &cursor_up},
       +        {NULL, XK_Down, INSERT|NORMAL, KEY_ANY, &cursor_down},
       +        {NULL, XK_Return, INSERT, KEY_ANY, &return_key},
       +        {NULL, XK_Delete, INSERT, KEY_ANY, &delete_key},
       +        {NULL, XK_Escape, INSERT, KEY_ANY, &escape_key},
       +        {"ि",  0, NORMAL, KEY_ANY, &enter_insert},
       +        {"ह",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       +        {"ल",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       +        {"ज",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       +        {"क",  0, NORMAL, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       +        {"0",  0, NORMAL, KEY_ANY, &cursor_to_beginning}
        };
        
        #define LENGTH(X) (sizeof(X) / sizeof(X[0]))