URI:
       tSupport highlighting entire words when searching and replacing - 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 b7f7a516c405f90fb1294a6b7cf4463373294cb0
   DIR parent 4e39cee7c2670c566dd05abc926f01aa8444e018
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat, 22 Oct 2022 11:47:14 +0200
       
       Support highlighting entire words when searching and replacing
       
       Diffstat:
         M buffer.c                            |       1 +
         M configparser.c                      |      29 +++++++++++++++++++++++++++++
         M configparser.h                      |       1 +
         M keys_basic.c                        |      14 ++++++++------
         M keys_command.c                      |      65 ++++++++++++++++++++++++-------
         M ledit.1                             |       2 --
         M leditrc.5                           |      16 +++++++++++-----
         M leditrc.example                     |       5 +++--
         M search.c                            |      21 +++++++++++++--------
         M search.h                            |       4 ++--
         M theme_config.h                      |       3 +++
         M util.c                              |       2 +-
         M util.h                              |       2 +-
         M view.c                              |      14 +++++++++++---
       
       14 files changed, 134 insertions(+), 45 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -274,6 +274,7 @@ buffer_set_hard_line_based(ledit_buffer *buffer, int hl) {
                buffer->hard_line_based = hl;
        }
        
       +/* FIXME: lock view if others are locked! */
        void
        buffer_add_view(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos, long scroll_offset) {
                size_t new_num = add_sz(buffer->views_num, 1);
   DIR diff --git a/configparser.c b/configparser.c
       t@@ -522,6 +522,34 @@ destroy_theme_string(ledit_common *common, void *obj) {
                free(*obj_str);
        }
        
       +static int
       +parse_theme_bool(
       +    ledit_common *common,
       +    void *obj, const char *val, size_t val_len, char *key,
       +    char *filename, size_t line, size_t line_offset, char **errstr) {
       +        (void)common;
       +        int *num = (int *)obj;
       +        if (str_array_equal("true", val, val_len)) {
       +                *num = 1;
       +                return 0;
       +        } else if (str_array_equal("false", val, val_len)) {
       +                *num = 0;
       +                return 0;
       +        }
       +        *errstr = print_fmt(
       +            "%s: Invalid boolean '%.*s' "
       +            "for '%s' at line %zu, position %zu",
       +            filename, val_len, val, key, line, line_offset
       +        );
       +        return 1;
       +}
       +
       +static void
       +destroy_theme_bool(ledit_common *common, void *obj) {
       +        (void)common;
       +        (void)obj;
       +}
       +
        /* FIXME: This interface is absolutely horrible - it's mainly this way to reuse the
           theme array for the destroy function */
        /* If theme is NULL, a new theme is loaded, else it is destroyed */
       t@@ -560,6 +588,7 @@ load_destroy_theme(ledit_common *common, ast_list *theme_list, ledit_theme *them
                        {"bar-fmt", &theme->bar_fmt, &parse_theme_string, &destroy_theme_string, BAR_FMT, default_init},
                        {"scrollbar-fg", &theme->scrollbar_fg, &parse_theme_color, &destroy_theme_color, SCROLLBAR_FG, default_init},
                        {"scrollbar-bg", &theme->scrollbar_bg, &parse_theme_color, &destroy_theme_color, SCROLLBAR_BG, default_init},
       +                {"highlight-search", &theme->highlight_search, &parse_theme_bool, &destroy_theme_bool, HIGHLIGHT_SEARCH, default_init},
                };
        
                if (default_init)
   DIR diff --git a/configparser.h b/configparser.h
       t@@ -10,6 +10,7 @@ typedef struct {
                int scrollbar_width;
                int scrollbar_step;
                int text_size;
       +        int highlight_search;
                XftColor text_fg;
                XftColor text_bg;
                XftColor cursor_fg;
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -1,3 +1,4 @@
       +/* FIXME: all movement commands that modify the selection should first check if the selection is valid! */
        /* FIXME: should motion callbacks be ignored in visual mode as they currently are? */
        /* FIXME: check allowed modes at beginning of functions */
        /* FIXME: the stacks here are shared for all views which can cause weird
       t@@ -180,8 +181,8 @@ static struct basic_key_cb basic_key_cb_map[] = {
                {"delete-to-eol", &delete_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT},
                {"enter-commandedit", &enter_commandedit, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
                {"enter-insert", &enter_insert, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL},
       -        {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
       -        {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
       +        {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL},
       +        {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL},
                {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT},
                {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
                {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
       t@@ -224,8 +225,8 @@ static struct basic_key_cb basic_key_cb_map[] = {
                {"scroll-lines-up", &scroll_lines_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
                {"scroll-with-cursor-down", &scroll_with_cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
                {"scroll-with-cursor-up", &scroll_with_cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
       -        {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
       -        {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT},
       +        {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL},
       +        {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL},
                {"show-line", &show_line, KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
                {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSOR, VISUAL},
                {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
       t@@ -695,8 +696,8 @@ delete_selection(ledit_view *view) {
                        );
                        paste_buffer_line_based = 0;
                        view->sel_valid = 0;
       -                view->sel.line1 = view->sel.line2 = 0;
       -                view->sel.byte1 = view->sel.byte2 = 0;
       +                view->sel.line1 = view->sel.line2 = view->cur_line;
       +                view->sel.byte1 = view->sel.byte2 = view->cur_index;
                        view_wipe_line_cursor_attrs(view, view->cur_line);
                        return 1;
                }
       t@@ -1797,6 +1798,7 @@ move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_index) {
                } else {
                        view->cur_index = new_index;
                        if (view->mode == VISUAL) {
       +                        /* FIXME: check if view->sel_valid and only use it then (also change in other places) */
                                view_set_selection(view, view->sel.line1, view->sel.byte1, view->cur_line, new_index);
                        } else if (view->mode == INSERT && view->sel_valid) {
                                /* FIXME: I guess this is unnecessary now that no
   DIR diff --git a/keys_command.c b/keys_command.c
       t@@ -355,20 +355,36 @@ next_replace_pos(
        /* returns whether keys should continue being captured */
        static int
        move_to_next_substitution(ledit_view *view) {
       -        view_wipe_line_cursor_attrs(view, view->cur_line);
       +        ledit_theme *theme = config_get_theme();
       +        if (view->mode == NORMAL)
       +                view_wipe_line_cursor_attrs(view, view->cur_line);
       +        else if (view->mode == VISUAL)
       +                view_wipe_selection(view);
                if (!next_replace_pos(view, sub_state.line, sub_state.byte, sub_state.max_line, &sub_state.line, &sub_state.byte)) {
       +                /* FIXME: why are these set here? */
                        view->cur_line = sub_state.line;
                        view->cur_index = sub_state.byte;
       -                if (view->mode == NORMAL)
       +                if (view->mode == NORMAL) {
       +                        view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index);
                                view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +                } else if (view->mode == VISUAL) {
       +                        view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
       +                }
                        window_show_message(view->window, "No more matches", -1);
                        buffer_unlock_all_views(view->buffer);
                        return 0;
                }
       +        if (theme->highlight_search && view->mode != VISUAL) {
       +                view_wipe_line_cursor_attrs(view, view->cur_line);
       +                view_set_mode(view, VISUAL);
       +        }
                view->cur_line = sub_state.line;
                view->cur_index = sub_state.byte;
       -        if (view->mode == NORMAL)
       +        if (view->mode == NORMAL) {
                        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +        } else if (view->mode == VISUAL && theme->highlight_search) {
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index + sub_state.slen);
       +        }
                window_show_message(view->window, "Replace? (y/Y/n/N)", -1);
                view_ensure_cursor_shown(view);
                return 1;
       t@@ -402,7 +418,10 @@ substitute_single(ledit_view *view) {
        
        static void
        substitute_all_remaining(ledit_view *view) {
       -        view_wipe_line_cursor_attrs(view, view->cur_line);
       +        if (view->mode == NORMAL)
       +                view_wipe_line_cursor_attrs(view, view->cur_line);
       +        else if (view->mode == VISUAL)
       +                view_wipe_selection(view);
                size_t min_line = SIZE_MAX;
                while (next_replace_pos(view, sub_state.line, sub_state.byte, sub_state.max_line, &sub_state.line, &sub_state.byte)) {
                        if (sub_state.line < min_line)
       t@@ -424,6 +443,8 @@ substitute_all_remaining(ledit_view *view) {
                        /* this doesn't need to be added to the undo stack since it's called on undo/redo anyways */
                        view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index);
                        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +        } else if (view->mode == VISUAL) {
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
                }
                view_ensure_cursor_shown(view);
                buffer_unlock_all_views(view->buffer);
       t@@ -462,6 +483,8 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
                        }
                        c++;
                }
       +        free(sep);
       +        sep = NULL;
                free(sub_state.search);
                free(sub_state.replace);
                sub_state.search = ledit_strdup(cmd);
       t@@ -477,12 +500,6 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
                sub_state.num = 0;
                sub_state.start_group = 1;
        
       -        /* trying to perform substitution in visual mode would make
       -           it unnecessarily complicated */
       -        if (view->mode == VISUAL) {
       -                view_wipe_selection(view);
       -                view_set_mode(view, NORMAL);
       -        }
                if (confirm) {
                        buffer_lock_all_views_except(view->buffer, view, "Ongoing substitution in other view.");
                        view->cur_command_type = CMD_SUBSTITUTE;
       t@@ -490,7 +507,6 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
                } else {
                        substitute_all_remaining(view);
                }
       -        free(sep);
                return 0;
        error:
                window_show_message(view->window, "Invalid command", -1);
       t@@ -871,13 +887,24 @@ edit_nextsearch(ledit_view *view, char *key_text, size_t len, size_t lang_index)
                return 1;
        }
        
       +/* FIXME: the current "highlight_search" support is a bit weird and will probably conflict
       +   in some way if other support for visual mode (e.g. only search in selection) is added */
        /* FIXME: support visual mode, i.e. change selection to new place? */
        void
        search_next(ledit_view *view) {
                view_wipe_line_cursor_attrs(view, view->cur_line);
       -        search_state ret = ledit_search_next(view, &view->cur_line, &view->cur_index);
       -        if (view->mode == NORMAL)
       +        size_t len = 0;
       +        search_state ret = ledit_search_next(view, &view->cur_line, &view->cur_index, &len);
       +        ledit_theme *theme = config_get_theme();
       +        /* FIXME: figure out key stack handling when modes are also changed here */
       +        if (theme->highlight_search && len > 0 && (ret == SEARCH_NORMAL || ret == SEARCH_WRAPPED)) {
       +                view_set_mode(view, VISUAL);
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index + len);
       +        } else if (view->mode == VISUAL) {
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
       +        } else if (view->mode == NORMAL) {
                        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +        }
                view_ensure_cursor_shown(view);
                if (ret != SEARCH_NORMAL)
                        window_show_message(view->window, search_state_to_str(ret), -1);
       t@@ -886,9 +913,17 @@ search_next(ledit_view *view) {
        void
        search_prev(ledit_view *view) {
                view_wipe_line_cursor_attrs(view, view->cur_line);
       -        search_state ret = ledit_search_prev(view, &view->cur_line, &view->cur_index);
       -        if (view->mode == NORMAL)
       +        size_t len = 0;
       +        search_state ret = ledit_search_prev(view, &view->cur_line, &view->cur_index, &len);
       +        ledit_theme *theme = config_get_theme();
       +        if (theme->highlight_search && len > 0 && (ret == SEARCH_NORMAL || ret == SEARCH_WRAPPED)) {
       +                view_set_mode(view, VISUAL);
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index + len);
       +        } else if (view->mode == VISUAL) {
       +                view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
       +        } if (view->mode == NORMAL) {
                        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +        }
                view_ensure_cursor_shown(view);
                if (ret != SEARCH_NORMAL)
                        window_show_message(view->window, search_state_to_str(ret), -1);
   DIR diff --git a/ledit.1 b/ledit.1
       t@@ -1,5 +1,3 @@
       -.\" WARNING: Some parts of this are stolen shamelessly from OpenBSD's
       -.\" vi(1) manpage!
        .Dd May 27, 2022
        .Dt LEDIT 1
        .Os
   DIR diff --git a/leditrc.5 b/leditrc.5
       t@@ -1,4 +1,4 @@
       -.Dd September 2, 2022
       +.Dd October 22, 2022
        .Dt LEDITRC 5
        .Os
        .Sh NAME
       t@@ -169,6 +169,12 @@ Default: #CCCCCC
        .It Ar scrollbar-fg
        Color of scrollbar handle.
        Default: #000000
       +.It Ar highlight-search
       +Whether entire words should be highlighted when searching or replacing with confirmation (true/false).
       +Note that the mode is automatically switched to visual when this is set and a word needs to be highlighted.
       +This is a bit weird, but in order to keep everything a bit more consistent, selections are curently
       +only allowed in visual mode.
       +Default: true
        .El
        .Sh BINDINGS
        The key bindings may be configured by assigning
       t@@ -423,10 +429,10 @@ In visual mode, the selection range is automatically pasted into the line editor
        so commands can be performed on it.
        .It Ar enter-insert Op normal, visual
        Enter insert mode.
       -.It Ar enter-searchedit-backwards Op normal, insert
       +.It Ar enter-searchedit-backwards Op normal, insert, visual
        Open the line editor for searching backwards.
        Note that no regex is currently supported.
       -.It Ar enter-searchedit-forwards Op normal, insert
       +.It Ar enter-searchedit-forwards Op normal, insert, visual
        Open the line editor for searching forwards.
        Note that no regex is currently supported.
        .It Ar enter-visual Op normal, insert
       t@@ -652,9 +658,9 @@ Move
        .Ar num
        lines up, attempting to leave the cursor in its current line and character position.
        Note that this command works with soft lines, regardless of the current mode.
       -.It Ar search-next Op normal, insert
       +.It Ar search-next Op normal, insert, visual
        Move to the next search result.
       -.It Ar search-previous Op normal, insert
       +.It Ar search-previous Op normal, insert, visual
        Move to the previous search result.
        .It Ar show-line Op normal, visual, insert
        Show the current file name, whether the buffer has been modified since the last
   DIR diff --git a/leditrc.example b/leditrc.example
       t@@ -18,6 +18,7 @@ theme = {
                scrollbar-step = 20
                scrollbar-bg = CCCCCC
                scrollbar-fg = 000000
       +        highlight-search = true
        }
        
        bindings = {
       t@@ -66,8 +67,8 @@ bindings = {
                        bind enter-commandedit text ":" modes normal|visual
                        bind enter-searchedit-backwards text "?" modes normal
                        bind enter-searchedit-forwards text "/" modes normal
       -                bind search-next text "n" modes normal
       -                bind search-previous text "N" modes normal
       +                bind search-next text "n" modes normal|visual
       +                bind search-previous text "N" modes normal|visual
                        bind undo text "u" modes normal
                        bind redo text "U" modes normal
                        bind repeat-command text "." modes normal
   DIR diff --git a/search.c b/search.c
       t@@ -7,6 +7,7 @@
        
        /* FIXME: make sure only whole utf8 chars are matched */
        static char *last_search = NULL;
       +static size_t last_search_len = 0;
        enum {
                FORWARD,
                BACKWARD
       t@@ -22,6 +23,7 @@ set_search_forward(char *pattern) {
                last_dir = FORWARD;
                free(last_search);
                last_search = ledit_strdup(pattern);
       +        last_search_len = strlen(last_search);
        }
        
        void
       t@@ -29,12 +31,14 @@ set_search_backward(char *pattern) {
                last_dir = BACKWARD;
                free(last_search);
                last_search = ledit_strdup(pattern);
       +        last_search_len = strlen(last_search);
        }
        
        static search_state
       -search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
       +search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
                *line_ret = view->cur_line;
                *byte_ret = view->cur_index;
       +        *len_ret = last_search_len;
                /* if last_search is empty, strstr will find the ending '\0' */
                if (last_search == NULL || last_search[0] == '\0')
                        return SEARCH_NO_PATTERN;
       t@@ -81,9 +85,10 @@ search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
        /* FIXME: this is insanely inefficient */
        /* FIXME: just go backwards char-by-char and compare */
        static search_state
       -search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
       +search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
                *line_ret = view->cur_line;
                *byte_ret = view->cur_index;
       +        *len_ret = last_search_len;
                if (last_search == NULL || last_search[0] == '\0')
                        return SEARCH_NO_PATTERN;
                size_t line = view->cur_line;
       t@@ -145,19 +150,19 @@ search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
        }
        
        search_state
       -ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
       +ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
                if (last_dir == FORWARD)
       -                return search_forward(view, line_ret, byte_ret);
       +                return search_forward(view, line_ret, byte_ret, len_ret);
                else
       -                return search_backward(view, line_ret, byte_ret);
       +                return search_backward(view, line_ret, byte_ret, len_ret);
        }
        
        search_state
       -ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
       +ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) {
                if (last_dir == FORWARD)
       -                return search_backward(view, line_ret, byte_ret);
       +                return search_backward(view, line_ret, byte_ret, len_ret);
                else
       -                return search_forward(view, line_ret, byte_ret);
       +                return search_forward(view, line_ret, byte_ret, len_ret);
        }
        
        char *
   DIR diff --git a/search.h b/search.h
       t@@ -13,8 +13,8 @@ typedef enum {
        void search_cleanup(void);
        void set_search_forward(char *pattern);
        void set_search_backward(char *pattern);
       -search_state ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret);
       -search_state ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret);
       +search_state ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret);
       +search_state ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret);
        
        /*
         * Get a string corresponding to a search_state.
   DIR diff --git a/theme_config.h b/theme_config.h
       t@@ -45,5 +45,8 @@ static const char *SCROLLBAR_STEP = "20";
        static const char *SCROLLBAR_BG = "#CCCCCC";
        /* color of scrollbar handle */
        static const char *SCROLLBAR_FG = "#000000";
       +/* whether to highlight entire words when searching/replacing */
       +/* FIXME: this should maybe be in a separate "general config" section */
       +static const char *HIGHLIGHT_SEARCH = "true";
        
        #endif /* _THEME_CONFIG_H_ */
   DIR diff --git a/util.c b/util.c
       t@@ -50,7 +50,7 @@ sort_range(size_t *l1, size_t *b1, size_t *l2, size_t *b2) {
        }
        
        int
       -str_array_equal(char *terminated, char *array, size_t len) {
       +str_array_equal(const char *terminated, const char *array, size_t len) {
                if (!strncmp(terminated, array, len)) {
                        /* this is kind of inefficient, but there's no way to know
                           otherwise if strncmp just stopped comparing after a '\0' */
   DIR diff --git a/util.h b/util.h
       t@@ -32,7 +32,7 @@ void sort_range(size_t *l1, size_t *b1, size_t *l2, size_t *b2);
         * Returns non-zero if they are equal, 0 otherwise.
         */
        /* Note: this doesn't work if array contains '\0'. */
       -int str_array_equal(char *terminated, char *array, size_t len);
       +int str_array_equal(const char *terminated, const char *array, size_t len);
        
        #define LEDIT_MIN(x, y) ((x) < (y) ? (x) : (y))
        #define LEDIT_MAX(x, y) ((x) > (y) ? (x) : (y))
   DIR diff --git a/view.c b/view.c
       t@@ -24,6 +24,9 @@
        #include "assert.h"
        #include "configparser.h"
        
       +/* FIXME: handle selections better - it can happen that the cursor is moved independently of
       +   the selection, leading to weird "jumping selection" - this should be made impossible */
       +
        /* Basic attributes set for all text. */
        static PangoAttrList *basic_attrs = NULL;
        
       t@@ -1746,9 +1749,14 @@ view_wipe_selection(ledit_view *view) {
                        }
                }
                view->sel_valid = 0;
       -        view->sel.line1 = view->sel.line2 = 0;
       -        view->sel.byte1 = view->sel.byte2 = 0;
       -        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +        /* FIXME: check what makes most sense here - this used to be set to 0, but that
       +           caused "jumping effects" because some functions (cursor movement) use these
       +           values without checking view->sel_valid before (which should actually be
       +           changed because it is an error) */
       +        view->sel.line1 = view->sel.line2 = view->cur_line;
       +        view->sel.byte1 = view->sel.byte2 = view->cur_index;
       +        if (view->mode == NORMAL)
       +                view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
        }
        
        void