URI: 
       tAdd rudimentary support for extra line spacing - 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 a3b601c93fde71dd598f855980897a1e3ead492e
   DIR parent 7a93ac1c6f53c9464eb548bef555f22da0d9bbac
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Mon,  2 Oct 2023 10:49:08 +0200
       
       Add rudimentary support for extra line spacing
       
       Diffstat:
         M configparser.c                      |      16 ++++++++++++++--
         M configparser.h                      |       1 +
         M keys_basic.c                        |       5 ++---
         M leditrc.5                           |       7 ++++++-
         M leditrc.example                     |       1 +
         M theme_config.h                      |       3 +++
         M view.c                              |      48 +++++++++++++++++--------------
       
       7 files changed, 54 insertions(+), 27 deletions(-)
       ---
   DIR diff --git a/configparser.c b/configparser.c
       t@@ -32,7 +32,7 @@ struct config {
                char **langs;
                size_t num_langs;
                size_t alloc_langs;
       -} config = {NULL};
       +} config = {NULL, NULL, NULL, NULL, NULL, 0, 0};
        
        enum toktype {
                STRING,
       t@@ -576,6 +576,7 @@ load_destroy_theme(ledit_common *common, ast_list *theme_list, ledit_theme *them
                        {"text-size", &theme->text_size, &parse_theme_number, &destroy_theme_number, TEXT_SIZE, default_init},
                        {"scrollbar-width", &theme->scrollbar_width, &parse_theme_number, &destroy_theme_number, SCROLLBAR_WIDTH, default_init},
                        {"scrollbar-step", &theme->scrollbar_step, &parse_theme_number, &destroy_theme_number, SCROLLBAR_STEP, default_init},
       +                {"extra-line-spacing", &theme->extra_line_spacing, &parse_theme_number, &destroy_theme_number, EXTRA_LINE_SPACING, default_init},
                        {"text-fg", &theme->text_fg, &parse_theme_color, &destroy_theme_color, TEXT_FG, default_init},
                        {"text-bg", &theme->text_bg, &parse_theme_color, &destroy_theme_color, TEXT_BG, default_init},
                        {"cursor-fg", &theme->cursor_fg, &parse_theme_color, &destroy_theme_color, CURSOR_FG, default_init},
       t@@ -664,6 +665,17 @@ load_destroy_theme(ledit_common *common, ast_list *theme_list, ledit_theme *them
                        }
                }
        
       +        /* FIXME: make this check part of the generic handling above (also, < 0 is already checked anyways) */
       +        /* FIXME: 100 is completely arbitrary */
       +        if (theme->extra_line_spacing < 0 || theme->extra_line_spacing > 100) {
       +                *errstr = print_fmt(
       +                    "%s: Invalid value '%d' for theme setting 'extra-line-spacing' "
       +                    "(allowed values are 0-100)",
       +                    filename, theme->extra_line_spacing
       +                );
       +                goto cleanup;
       +        }
       +
                return theme;
        cleanup:
                for (size_t i = 0; i < LENGTH(settings); i++) {
       t@@ -1658,7 +1670,7 @@ config_loadfile(ledit_common *common, char *filename, char **errstr) {
                clock_gettime(CLOCK_MONOTONIC, &last);
                #endif
        
       -        struct config cfg = {NULL};
       +        struct config cfg = {NULL, NULL, NULL, NULL, NULL, 0, 0};
                int theme_init = 0, bindings_init = 0, mappings_init = 0;
                ast_assignment *assignment;
                for (size_t i = 0; i < list.len; i++) {
   DIR diff --git a/configparser.h b/configparser.h
       t@@ -11,6 +11,7 @@ typedef struct {
                int scrollbar_step;
                int text_size;
                int highlight_search;
       +        int extra_line_spacing;
                XftColor text_fg;
                XftColor text_bg;
                XftColor cursor_fg;
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -714,10 +714,9 @@ static void
        push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group) {
                /* WARNING: Don't abuse txtbuf like this unless you're stupid like me. */
                txtbuf ins_buf = {.text = "", .len = 0, .cap = 0};
       -        ledit_range ins_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
       -        ledit_range cur_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
       +        ledit_range range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
                undo_push_insert(
       -            view->buffer->undo, &ins_buf, ins_range, cur_range, start_group, view->mode
       +            view->buffer->undo, &ins_buf, range, range, start_group, view->mode
                );
        }
        
   DIR diff --git a/leditrc.5 b/leditrc.5
       t@@ -1,4 +1,4 @@
       -.Dd October 22, 2022
       +.Dd October 2, 2023
        .Dt LEDITRC 5
        .Os
        .Sh NAME
       t@@ -175,6 +175,11 @@ Note that the mode is automatically switched to visual when this is set and a wo
        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
       +.It Ar extra-line-spacing
       +Extra space between each line (in pixels).
       +Note that this is very rudimentary at the moment.
       +In particular, selections covering multiple lines do not highlight the extra space.
       +Default: 0
        .El
        .Sh BINDINGS
        The key bindings may be configured by assigning
   DIR diff --git a/leditrc.example b/leditrc.example
       t@@ -19,6 +19,7 @@ theme = {
                scrollbar-bg = CCCCCC
                scrollbar-fg = 000000
                highlight-search = true
       +        extra-line-spacing = 0
        }
        
        bindings = {
   DIR diff --git a/theme_config.h b/theme_config.h
       t@@ -49,4 +49,7 @@ static const char *SCROLLBAR_FG = "#000000";
        /* FIXME: this should maybe be in a separate "general config" section */
        static const char *HIGHLIGHT_SEARCH = "true";
        
       +/* extra space between lines (in pixels) */
       +static const char *EXTRA_LINE_SPACING = "0";
       +
        #endif /* _THEME_CONFIG_H_ */
   DIR diff --git a/view.c b/view.c
       t@@ -492,6 +492,7 @@ invalidate_layout_line_helper(void *data, size_t line) {
        
        void
        view_recalc_line(ledit_view *view, size_t line) {
       +        ledit_theme *theme = config_get_theme();
                ledit_view_line *l = view_get_line(view, line);
                if (l->text_dirty)
                        set_pango_text_and_highlight(view, line);
       t@@ -503,13 +504,13 @@ view_recalc_line(ledit_view *view, size_t line) {
                if (l->h_dirty) {
                        l->h_dirty = 0;
                        /* FIXME: maybe also check overflow for offset? */
       -                long off = l->y_offset + l->h;
       +                long off = l->y_offset + l->h + theme->extra_line_spacing;
                        for (size_t i = line + 1; i < view->lines_num; i++) {
                                l = view_get_line(view, i);
                                l->y_offset = off;
       -                        off += l->h;
       +                        off += l->h + theme->extra_line_spacing;
                        }
       -                view->total_height = off;
       +                view->total_height = off - theme->extra_line_spacing;
                        if (l->y_offset < view->display_offset + text_h)
                                view->redraw = 1;
                }
       t@@ -524,6 +525,7 @@ view_recalc_line(ledit_view *view, size_t line) {
        
        void
        view_recalc_from_line(ledit_view *view, size_t line) {
       +        ledit_theme *theme = config_get_theme();
                ledit_view_line *l = view_get_line(view, line);
                /* force first line to offset 0 */
                if (line == 0)
       t@@ -539,9 +541,9 @@ view_recalc_from_line(ledit_view *view, size_t line) {
                                set_pango_text_and_highlight(view, i);
                        l->h_dirty = 0;
                        l->y_offset = off;
       -                off += l->h;
       +                off += l->h + theme->extra_line_spacing;
                }
       -        view->total_height = off;
       +        view->total_height = off - theme->extra_line_spacing;
                window_set_scroll_max(view->window, view->total_height);
                view_scroll(view, view->display_offset);
        }
       t@@ -1071,6 +1073,7 @@ ledit_line_word_boundaries(ledit_line *line, int byte, int *start_ret, int *end_
        static void
        set_pango_text_and_highlight(ledit_view *view, size_t line) {
                cache_layout *cl;
       +        ledit_theme *theme = config_get_theme();
                ledit_line *ll = buffer_get_line(view->buffer, line);
                ledit_view_line *vl = view_get_line(view, line);
                char old_valid = vl->cache_layout_valid;
       t@@ -1087,6 +1090,7 @@ set_pango_text_and_highlight(ledit_view *view, size_t line) {
                        cl->layout = pango_layout_new(view->window->context);
                        pango_layout_set_font_description(cl->layout, view->window->font);
                        pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR);
       +                pango_layout_set_spacing(cl->layout, theme->extra_line_spacing * PANGO_SCALE);
                }
                if (vl->text_dirty || !old_valid) {
                        buffer_normalize_line(ll);
       t@@ -1512,6 +1516,7 @@ view_delete_range_base(
        /* FIXME: any way to make this more efficient? */
        void
        view_resize_textview(void *data) {
       +        ledit_theme *theme = config_get_theme();
                ledit_view *view = (ledit_view *)data;
                view->total_height = 0;
                int text_w, text_h;
       t@@ -1524,8 +1529,9 @@ view_resize_textview(void *data) {
                        line->y_offset = view->total_height;
                        line->dirty = 1;
                        line->h_dirty = 0;
       -                view->total_height += line->h;
       +                view->total_height += line->h + theme->extra_line_spacing;
                }
       +        view->total_height -= theme->extra_line_spacing;
                window_set_scroll_max(view->window, view->total_height);
                if (view->display_offset > 0 &&
                    view->display_offset + text_h > view->total_height) {
       t@@ -1547,6 +1553,7 @@ view_scroll(ledit_view *view, long new_offset) {
        
        /* FIXME: there's gotta be a better/more efficient way to do this... */
        /* FIXME: make sure h_dirty is not set here */
       +/* FIXME: this might cause weird effects when used together with extra-line-spacing */
        void
        view_get_nearest_legal_pos(
            ledit_view *view,
       t@@ -1648,19 +1655,19 @@ view_get_nearest_legal_pos(
        
        void
        view_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest, size_t *line_ret, size_t *byte_ret) {
       -        /* FIXME: store current line offset to speed this up */
       -        /* FIXME: use y_offset in lines */
       -        long h = 0;
       +        ledit_theme *theme = config_get_theme();
                long pos = view->display_offset + y;
                for (size_t i = 0; i < view->lines_num; i++) {
                        ledit_view_line *vline = view_get_line(view, i);
       -                if ((h <= pos && h + vline->h > pos) || i == view->lines_num - 1) {
       +                long y1 = vline->y_offset - (i == 0 ? 0 : theme->extra_line_spacing / 2);
       +                long y2 = vline->y_offset + vline->h + theme->extra_line_spacing / 2 + theme->extra_line_spacing % 2;
       +                if ((y1 <= pos && y2 > pos) || i == view->lines_num - 1) {
                                int index, trailing;
                                PangoLayout *layout = get_pango_layout(view, i);
                                /* FIXME: what if i == view->lines_num - 1 but pos - h < 0? */
                                pango_layout_xy_to_index(
                                    layout,
       -                            x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
       +                            x * PANGO_SCALE, (int)(pos - y1) * PANGO_SCALE,
                                    &index, &trailing
                                );
                                *byte_ret = (size_t)index;
       t@@ -1674,7 +1681,6 @@ view_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest, size_t
                                *line_ret = i;
                                return;
                        }
       -                h += vline->h;
                }
                *line_ret = 0;
                *byte_ret = 0;
       t@@ -1894,15 +1900,16 @@ view_button_handler(void *data, XEvent *event) {
        static void
        view_redraw_text(ledit_view *view) {
                ledit_theme *theme = config_get_theme();
       -        int h = 0;
                int cur_line_y = 0;
                int cursor_displayed = 0;
                int text_w, text_h;
                window_get_textview_size(view->window, &text_w, &text_h);
                /* FIXME: use binary search here */
       +        /* FIXME: draw extra highlight when extra-line-spacing set
       +           (also between soft lines because pango doesn't do that) */
                for (size_t i = 0; i < view->lines_num; i++) {
                        ledit_view_line *vline = view_get_line(view, i);
       -                if (h + vline->h > view->display_offset) {
       +                if (vline->y_offset + vline->h > view->display_offset) {
                                /* FIXME: vline->text_dirty should not happen here */
                                if (vline->text_dirty || vline->highlight_dirty)
                                        set_pango_text_and_highlight(view, i);
       t@@ -1910,12 +1917,12 @@ view_redraw_text(ledit_view *view) {
                                        render_line(view, i);
                                }
                                int final_y = 0;
       -                        int dest_y = h - view->display_offset;
       +                        int dest_y = vline->y_offset - view->display_offset;
                                int final_h = vline->h;
       -                        if (h < view->display_offset) {
       +                        if (vline->y_offset < view->display_offset) {
                                        dest_y = 0;
       -                                final_y = view->display_offset - h;
       -                                final_h -= view->display_offset - h;
       +                                final_y = view->display_offset - vline->y_offset;
       +                                final_h -= view->display_offset - vline->y_offset;
                                }
                                if (dest_y + final_h > text_h) {
                                        final_h -= final_y + final_h -
       t@@ -1930,13 +1937,12 @@ view_redraw_text(ledit_view *view) {
                                    0, final_y, vline->w, final_h, 0, dest_y
                                );
                                if (i == view->cur_line) {
       -                                cur_line_y = h - view->display_offset;
       +                                cur_line_y = vline->y_offset - view->display_offset;
                                        cursor_displayed = 1;
                                }
       -                        if (h + vline->h >= view->display_offset + text_h)
       +                        if (vline->y_offset + vline->h >= view->display_offset + text_h)
                                        break;
                        }
       -                h += vline->h;
                }
        
                XSetForeground(view->buffer->common->dpy, view->window->gc, theme->cursor_bg.pixel);