ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit a3b601c93fde71dd598f855980897a1e3ead492e
parent 7a93ac1c6f53c9464eb548bef555f22da0d9bbac
Author: lumidify <nobody@lumidify.org>
Date:   Mon,  2 Oct 2023 10:49:08 +0200

Add rudimentary support for extra line spacing

Diffstat:
Mconfigparser.c | 16++++++++++++++--
Mconfigparser.h | 1+
Mkeys_basic.c | 5++---
Mleditrc.5 | 7++++++-
Mleditrc.example | 1+
Mtheme_config.h | 3+++
Mview.c | 48+++++++++++++++++++++++++++---------------------
7 files changed, 54 insertions(+), 27 deletions(-)

diff --git a/configparser.c b/configparser.c @@ -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, @@ -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}, @@ -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++) { @@ -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++) { diff --git a/configparser.h b/configparser.h @@ -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; diff --git a/keys_basic.c b/keys_basic.c @@ -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 ); } diff --git a/leditrc.5 b/leditrc.5 @@ -1,4 +1,4 @@ -.Dd October 22, 2022 +.Dd October 2, 2023 .Dt LEDITRC 5 .Os .Sh NAME @@ -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 diff --git a/leditrc.example b/leditrc.example @@ -19,6 +19,7 @@ theme = { scrollbar-bg = CCCCCC scrollbar-fg = 000000 highlight-search = true + extra-line-spacing = 0 } bindings = { diff --git a/theme_config.h b/theme_config.h @@ -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_ */ diff --git a/view.c b/view.c @@ -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); @@ -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; } @@ -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) @@ -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); } @@ -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; @@ -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); @@ -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; @@ -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) { @@ -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, @@ -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; @@ -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; @@ -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); @@ -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 - @@ -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);