URI: 
       tImprove file handling - 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 22ffb413e9e0be6a5764de4bcc951e6a0480bad3
   DIR parent b8557a969d0da663fe3175d19269cb2f37eb9e72
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri, 10 Dec 2021 20:23:49 +0100
       
       Improve file handling
       
       Diffstat:
         M buffer.c                            |      16 ++++++++++++----
         M buffer.h                            |       2 ++
         M keys_basic.c                        |      22 ++++++++++++++--------
         M keys_command.c                      |      81 +++++++++++++++++++++++++------
         M ledit.c                             |      57 +++++++++++++++++++++++++------
         M theme_config.h                      |       1 +
         M window.c                            |      96 ++++++++++++++++++++++++-------
         M window.h                            |      14 ++++++++++++++
       
       8 files changed, 231 insertions(+), 58 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -191,8 +191,8 @@ buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t line, si
                }
                if (marklist->len == marklist->alloc) {
                        size_t new_alloc = marklist->alloc > 0 ? marklist->alloc * 2 : 4;
       -                marklist->marks = ledit_realloc(
       -                    marklist->marks, new_alloc * sizeof(ledit_buffer_mark)
       +                marklist->marks = ledit_reallocarray(
       +                    marklist->marks, new_alloc, sizeof(ledit_buffer_mark)
                        );
                        marklist->alloc = new_alloc;
                }
       t@@ -248,6 +248,7 @@ buffer_create(ledit_common *common) {
                buffer->marklist = marklist_create();
        
                buffer->filename = NULL;
       +        memset(&buffer->file_mtime, 0, sizeof(buffer->file_mtime));
                buffer->lines = NULL;
                buffer->lines_num = 0;
                buffer->lines_cap = 0;
       t@@ -255,6 +256,7 @@ buffer_create(ledit_common *common) {
                buffer->views = NULL;
                buffer->views_num = 0;
                buffer->hard_line_based = 1;
       +        buffer->modified = 0;
        
                /* add one empty line to buffer */
                resize_and_move_line_gap(buffer, 1, 0);
       t@@ -337,7 +339,6 @@ buffer_recalc_all_views_from_line(ledit_buffer *buffer, size_t line) {
                }
        }
        
       -/* FIXME: don't generate extra blank line at end! */
        /* WARNING: errstr must be copied as soon as possible! */
        int
        buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errstr) {
       t@@ -355,6 +356,7 @@ buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errst
                if (fseek(file, 0, SEEK_SET)) goto errorclose;
        
                ll = buffer_get_line(buffer, line);
       +        /* FIXME: insert in chunks instead of allocating huge buffer */
                file_contents = ledit_malloc(len + 2);
                /* mimic nvi (or at least the openbsd version) - if the line
                   is empty, insert directly, otherwise insert after the line */
       t@@ -367,7 +369,7 @@ buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errst
                if (ferror(file)) goto errorclose;
                file_contents[len + off] = '\0';
                /* don't generate extra newline at end */
       -        if (len + off > 1 && file_contents[len + off - 1 == '\n']) {
       +        if (len + off > 0 && file_contents[len + off - 1] == '\n') {
                        file_contents[len + off - 1] = '\0';
                        len--;
                }
       t@@ -377,6 +379,7 @@ buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errst
                    buffer, line, ll->len, file_contents, len + off, NULL, NULL
                );
                free(file_contents);
       +        buffer->modified = 0;
                return 0;
        error:
                if (*errstr)
       t@@ -399,6 +402,7 @@ buffer_write_to_file(ledit_buffer *buffer, FILE *file, char **errstr) {
                        if (fprintf(file, "%s\n", ll->text) < 0) goto errorclose;
                }
                if (fclose(file)) goto error;
       +        buffer->modified = 0;
                return 0;
        error:
                if (*errstr)
       t@@ -951,6 +955,7 @@ buffer_undo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line, size_t
                            buffer, min_line > 0 ? min_line - 1 : min_line
                        );
                }
       +        buffer->modified = 1;
        }
        
        void
       t@@ -965,6 +970,7 @@ buffer_redo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line, size_t
                            buffer, min_line > 0 ? min_line - 1 : min_line
                        );
                }
       +        buffer->modified = 1;
        }
        
        void
       t@@ -989,6 +995,7 @@ buffer_delete_with_undo_base(
                );
                if (text_ret == NULL)
                        txtbuf_destroy(buf);
       +        buffer->modified = 1;
        }
        
        void
       t@@ -1039,6 +1046,7 @@ buffer_insert_with_undo_base(
                        *line_ret = new_line;
                if (byte_ret != NULL)
                        *byte_ret = new_byte;
       +        buffer->modified = 1;
        }
        
        void
   DIR diff --git a/buffer.h b/buffer.h
       t@@ -28,6 +28,7 @@ typedef struct {
        struct ledit_buffer {
                ledit_common *common;            /* common stuff, e.g. display, etc. */
                char *filename;                  /* last opened filename */
       +        struct timespec file_mtime;      /* last modified time of file */
                undo_stack *undo;                /* undo manager */
                ledit_buffer_marklist *marklist; /* list of mark positions set */
                ledit_line *lines;               /* array of lines */
       t@@ -36,6 +37,7 @@ struct ledit_buffer {
                size_t lines_cap;                /* size of lines array */
                size_t lines_gap;                /* position of gap for line gap buffer */
                size_t lines_num;                /* number of lines */
       +        int modified;                    /* whether buffer was modified since loading a file */
                int hard_line_based;             /* whether operations should work on soft- or hardlines
                                                    Note that this doesn't actually change any behavior of
                                                    the buffer functions, it is just stored here so all
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -147,8 +147,8 @@ push_key_stack(void) {
                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.stack = ledit_reallocarray(
       +                    key_stack.stack, new_alloc, sizeof(struct key_stack_elem)
                        );
                        key_stack.alloc = new_alloc;
                }
       t@@ -275,9 +275,9 @@ push_repetition_stack(void) {
                struct repetition_stack_elem *e;
                if (repetition_stack.tmp_len >= repetition_stack.tmp_alloc) {
                        size_t new_alloc = repetition_stack.tmp_alloc > 0 ? repetition_stack.tmp_alloc * 2 : 4;
       -                repetition_stack.tmp_stack = ledit_realloc(
       +                repetition_stack.tmp_stack = ledit_reallocarray(
                            repetition_stack.tmp_stack,
       -                    new_alloc * sizeof(struct repetition_stack_elem)
       +                    new_alloc, sizeof(struct repetition_stack_elem)
                        );
                        for (size_t i = repetition_stack.tmp_alloc; i < new_alloc; i++) {
                                repetition_stack.tmp_stack[i].key_text = NULL;
       t@@ -2281,13 +2281,19 @@ basic_key_handler(ledit_view *view, XEvent *event, int lang_index) {
                re->key_state = key_state;
                re->lang_index = lang_index;
        
       -        /* FIXME: only hide when actually necessary */
       -        window_hide_message(view->window);
       +        /* FIXME: figure out when to actually hide message and
       +           ensure cursor shown */
       +        /* FIXME: clean up interface (what has a setter method and what not) */
                int found = 0;
       +        int msg_shown = view->window->message_shown;
       +        view->window->message_shown = 0; /* FIXME: this is hacky */
                struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found);
       +        if (found && n > 0 && !view->window->message_shown)
       +                window_hide_message(view->window);
       +        else
       +                view->window->message_shown = msg_shown;
        
       -        /* FIXME: only do this when necessary */
       -        if (found)
       +        if (found && n > 0)
                        view_ensure_cursor_shown(view);
                /* FIXME: maybe show error if not found */
                return act;
   DIR diff --git a/keys_command.c b/keys_command.c
       t@@ -2,6 +2,8 @@
        #include <stdio.h>
        #include <ctype.h>
        #include <stdlib.h>
       +#include <stdint.h>
       +#include <unistd.h>
        
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       t@@ -109,28 +111,74 @@ static int handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2);
        static int parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid);
        static int handle_cmd(ledit_view *view, char *cmd, size_t len);
        
       +/* FIXME: remove command name before passing to handlers */
        static int
        handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
       -        (void)view;
       -        (void)cmd;
                (void)l1;
                (void)l2;
       -        /* FIXME: Implement properly; handle error */
       +        /* FIXME: allow writing only part of file */
       +        char *filename = view->buffer->filename;
       +        int stored = 1;
       +        cmd++; /* remove 'w' */
       +        int force = 0;
       +        if (*cmd == '!') {
       +                force = 1;
       +                cmd++;
       +        }
       +        /* FIXME: string parsing instead of just taking the rest of the line */
       +        if (cmd[0] == ' ' && cmd[1] != '\0') {
       +                filename = cmd + 1;
       +                stored = 0;
       +        }
       +        /* FIXME: file locks */
                char *errstr;
       -        if (view->buffer->filename)
       -                buffer_write_to_filename(view->buffer, view->buffer->filename, &errstr);
       +        if (filename) {
       +                struct stat sb;
       +                /* There technically is a race between checking stat and actually
       +                   trying to write the file, but I don't care at the moment. */
       +                int ret = 0;
       +                if (!(ret = stat(filename, &sb)) && !force && stored &&
       +                    (sb.st_mtim.tv_sec != view->buffer->file_mtime.tv_sec ||
       +                     sb.st_mtim.tv_nsec != view->buffer->file_mtime.tv_nsec)) {
       +                        window_show_message_fmt(
       +                            view->window,
       +                            "%s: file modification time changed; use ! to override",
       +                            filename
       +                        );
       +                /* FIXME: I guess the file can still exist if stat returns an error,
       +                   but the writing itself will probably fail then as well. */
       +                } else if (!ret && !force && !stored) {
       +                        window_show_message_fmt(
       +                            view->window,
       +                            "%s: file exists; use ! to override",
       +                            filename
       +                        );
       +                } else if (buffer_write_to_filename(view->buffer, filename, &errstr)) {
       +                        window_show_message_fmt(view->window, "Error writing %s: %s", filename, errstr);
       +                } else {
       +                        /* FIXME: better message */
       +                        window_show_message_fmt(view->window, "Wrote file %s", filename);
       +                }
       +        } else {
       +                window_show_message(view->window, "No file name", -1);
       +        }
                return 0;
        }
        
        static int
        handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
       -        (void)view;
       -        (void)cmd;
                (void)l1;
                (void)l2;
       -        /* FIXME: ask to save changes */
       -        ledit_cleanup();
       -        exit(0);
       +        cmd++;
       +        int force = 0;
       +        if (*cmd == '!')
       +                force = 1;
       +        if (view->buffer->modified && !force) {
       +                window_show_message(view->window, "File modified; write or use ! to override", -1);
       +        } else {
       +                ledit_cleanup();
       +                exit(0);
       +        }
                return 0;
        }
        
       t@@ -161,11 +209,9 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
        
        static int
        handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
       -        (void)view;
       -        (void)cmd;
       -        (void)l1;
       -        (void)l2;
       -        printf("write quit\n");
       +        handle_write(view, cmd + 1, l1, l2);
       +        ledit_cleanup();
       +        exit(0);
                return 0;
        }
        
       t@@ -633,7 +679,10 @@ edit_submit(ledit_view *view, char *key_text, size_t len) {
                        text += min_pos;
                }
                /* FIXME: this is hacky */
       -        return handle_cmd(view, text, (size_t)textlen);
       +        char *cmd = ledit_strndup(text, textlen);
       +        int ret = handle_cmd(view, cmd, (size_t)textlen);
       +        free(cmd);
       +        return ret;
        }
        
        static int
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -234,21 +234,58 @@ setup(int argc, char *argv[]) {
        
                theme = theme_create(&common);
                buffer = buffer_create(&common);
       +        buffer_add_view(buffer, theme, NORMAL, 0, 0, 0);
       +        /* FIXME: don't access view directly here */
       +        ledit_view *view = buffer->views[0];
       +        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
                /* FIXME: Support multiple buffers/files */
       +        /* FIXME: check if file may be binary */
                if (argc > 1) {
       +                /* FIXME: move this to different file */
                        char *load_err;
       -                if (buffer_load_file(buffer, argv[1], 0, &load_err)) {
       -                        fprintf(stderr, "Error opening file '%s': %s\n", argv[1], load_err);
       -                        ledit_cleanup();
       -                        exit(1);
       +                struct stat sb;
       +                int newfile = 0;
       +                int readonly = 0;
       +                int error = 0;
       +                /* FIXME: maybe copy vi and open file in /tmp by default? */
       +                if (stat(argv[1], &sb)) {
       +                        if (errno == ENOENT) {
       +                                /* note that there may still be a failure
       +                                   when trying to write if a directory in
       +                                   the path does not exist */
       +                                newfile = 1;
       +                        } else {
       +                                window_show_message_fmt(
       +                                    view->window, "Error opening file '%s': %s",
       +                                    argv[1], strerror(errno)
       +                                );
       +                                error = 1;
       +                        }
       +                }
       +                if (access(argv[1], W_OK)) {
       +                        readonly = 1;
       +                }
       +                if (!newfile) {
       +                        if (buffer_load_file(buffer, argv[1], 0, &load_err)) {
       +                                window_show_message_fmt(
       +                                    view->window, "Error opening file '%s': %s",
       +                                    argv[1], load_err
       +                                );
       +                                error = 1;
       +                        }
       +                        buffer->file_mtime = sb.st_mtim;
       +                }
       +                if (!error) {
       +                        buffer->filename = ledit_strdup(argv[1]);
       +                        if (newfile) {
       +                                window_show_message_fmt(view->window, "%s: new file", argv[1]);
       +                        } else if (readonly) {
       +                                window_show_message_fmt(view->window, "%s: readonly", argv[1]);
       +                        } else {
       +                                window_show_message(view->window, argv[1], -1);
       +                        }
                        }
       -                /* FIXME: encapsulate */
       -                buffer->filename = ledit_strdup(argv[1]);
                }
       -        buffer_add_view(buffer, theme, NORMAL, 0, 0, 0);
       -        /* FIXME: don't access view directly here */
       -        ledit_view *view = buffer->views[0];
       -        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
        
                redraw();
        }
   DIR diff --git a/theme_config.h b/theme_config.h
       t@@ -1,3 +1,4 @@
       +/* FIXME: different bg for bottom bar */
        /* FIXME: configure font here */
        static const int TEXT_SIZE = 12;
        static const char *TEXT_FG = "#000000";
   DIR diff --git a/window.c b/window.c
       t@@ -3,6 +3,7 @@
        #include <math.h>
        #include <stdio.h>
        #include <stdlib.h>
       +#include <stdarg.h>
        
        #include <X11/Xlib.h>
        #include <X11/Xatom.h>
       t@@ -88,16 +89,44 @@ window_get_primary_clipboard_buffer(void) {
                return xsel.primary;
        }
        
       -/* FIXME static void ensure_cursor_shown(void); */
       -
        /* FIXME: shouldn't window->bottom_text_shown also be true when message_shown? */
       +/* FIXME: guard against negative width/height */
        static void
        recalc_text_size(ledit_window *window) {
                int bar_h = window->bb->mode_h;
       -        if ((window->bottom_text_shown || window->message_shown) && window->bb->line_h > bar_h)
       +        if (window->bottom_text_shown || window->message_shown)
                        bar_h = window->bb->line_h;
                window->text_w = window->w - window->theme->scrollbar_width;
                window->text_h = window->h - bar_h;
       +        if (window->text_w < 0)
       +                window->text_w = 0;
       +        if (window->text_h < 0)
       +                window->text_h = 0;
       +}
       +
       +static void
       +resize_line_text(ledit_window *window, int min_size) {
       +        if (min_size > window->bb->line_alloc || window->bb->line_text == NULL) {
       +                /* FIXME: read up on what the best values are here */
       +                /* FIXME: overflow */
       +                window->bb->line_alloc =
       +                    window->bb->line_alloc * 2 > min_size ?
       +                    window->bb->line_alloc * 2 :
       +                    min_size;
       +                window->bb->line_text = ledit_realloc(window->bb->line_text, window->bb->line_alloc);
       +        }
       +}
       +
       +static void
       +redraw_line_text(ledit_window *window) {
       +        /* FIXME: set_text doesn't really belong here */
       +        pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
       +        pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
       +        draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
       +        XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->line_w, window->bb->line_h);
       +        pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->text_fg, window->bb->line, 0, 0);
       +        recalc_text_size(window);
       +        window->redraw = 1;
        }
        
        /* FIXME: allow lines longer than window width to be displayed properly */
       t@@ -109,14 +138,7 @@ window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
                if (len == -1)
                        len = strlen(text);
                /* \0 not included in len */
       -        if (window->bb->line_len + len + 1 > window->bb->line_alloc || window->bb->line_text == NULL) {
       -                /* FIXME: read up on what the best values are here */
       -                window->bb->line_alloc =
       -                    window->bb->line_alloc * 2 > window->bb->line_len + len + 1 ?
       -                    window->bb->line_alloc * 2 :
       -                    window->bb->line_len + len + 1;
       -                window->bb->line_text = ledit_realloc(window->bb->line_text, window->bb->line_alloc);
       -        }
       +        resize_line_text(window, window->bb->line_len + len + 1);
                memmove(
                    window->bb->line_text + window->bb->line_cur_pos + len,
                    window->bb->line_text + window->bb->line_cur_pos,
       t@@ -125,13 +147,7 @@ window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
                memcpy(window->bb->line_text + window->bb->line_cur_pos, text, len);
                window->bb->line_len += len;
                window->bb->line_text[window->bb->line_len] = '\0';
       -        pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
       -        pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
       -        draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
       -        XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->line_w, window->bb->line_h);
       -        pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->text_fg, window->bb->line, 0, 0);
       -        recalc_text_size(window);
       -        window->redraw = 1;
       +        redraw_line_text(window);
        }
        
        void
       t@@ -243,12 +259,17 @@ void
        window_set_bottom_bar_text_shown(ledit_window *window, int shown) {
                window->bottom_text_shown = shown;
                window->redraw = 1;
       +        if (shown) {
       +                window->message_shown = 0;
       +                pango_layout_set_width(window->bb->line, -1);
       +                redraw_line_text(window);
       +                recalc_text_size(window);
       +        }
        }
        
        int
        ledit_window_bottom_bar_text_shown(ledit_window *window) {
                return window->bottom_text_shown;
       -        window->redraw = 1;
        }
        
        void
       t@@ -275,6 +296,7 @@ window_get_bottom_bar_text(ledit_window *window) {
        
        void
        window_show_message(ledit_window *window, char *text, int len) {
       +        pango_layout_set_width(window->bb->line, window->w * PANGO_SCALE);
                window_set_bottom_bar_text(window, text, len);
                /* FIXME: rename these */
                window->bottom_text_shown = 0;
       t@@ -282,6 +304,26 @@ window_show_message(ledit_window *window, char *text, int len) {
                window->redraw = 1;
        }
        
       +void
       +window_show_message_fmt(ledit_window *window, char *fmt, ...) {
       +        va_list args;
       +        va_start(args, fmt);
       +        int len = vsnprintf(window->bb->line_text, window->bb->line_alloc, fmt, args);
       +        if (len >= window->bb->line_alloc) {
       +                va_end(args);
       +                va_start(args, fmt);
       +                /* +1 because of terminating '\0' */
       +                resize_line_text(window, len + 1);
       +                vsnprintf(window->bb->line_text, window->bb->line_alloc, fmt, args);
       +        }
       +        window->bb->line_len = len;
       +        va_end(args);
       +        pango_layout_set_width(window->bb->line, window->w * PANGO_SCALE);
       +        window->bottom_text_shown = 0;
       +        window->message_shown = 1;
       +        redraw_line_text(window);
       +}
       +
        int
        window_message_shown(ledit_window *window) {
                return window->message_shown;
       t@@ -291,6 +333,7 @@ void
        window_hide_message(ledit_window *window) {
                window->message_shown = 0;
                window->redraw = 1;
       +        recalc_text_size(window);
        }
        
        void
       t@@ -543,6 +586,14 @@ window_create(ledit_common *common, ledit_theme *theme, enum ledit_mode mode) {
                window->bb->mode_draw = draw_create(window, 10, 10);
                window->bb->line = pango_layout_new(window->context);
                pango_layout_set_font_description(window->bb->line, window->font);
       +        pango_layout_set_wrap(window->bb->line, PANGO_WRAP_WORD_CHAR);
       +        #if PANGO_VERSION_CHECK(1, 44, 0)
       +        PangoAttrList *pattrs = pango_attr_list_new();
       +        PangoAttribute *no_hyphens = pango_attr_insert_hyphens_new(FALSE);
       +        pango_attr_list_insert(pattrs, no_hyphens);
       +        pango_layout_set_attributes(window->bb->line, pattrs);
       +        pango_attr_list_unref(pattrs);
       +        #endif
                window->bb->line_draw = draw_create(window, 10, 10);
                window->bb->line_w = window->bb->line_h = 10;
                window->bb->line_text = NULL;
       t@@ -766,7 +817,12 @@ void
        window_resize(ledit_window *window, int w, int h) {
                window->w = w;
                window->h = h;
       -        recalc_text_size(window);
       +        if (window->message_shown) {
       +                pango_layout_set_width(window->bb->line, window->w * PANGO_SCALE);
       +                redraw_line_text(window);
       +        } else {
       +                recalc_text_size(window);
       +        }
                if (window->resize_callback)
                        window->resize_callback(window->resize_cb_data);
                window->redraw = 1;
   DIR diff --git a/window.h b/window.h
       t@@ -5,6 +5,11 @@
         * partially here and partially in keys_command, but that's the way it is for now.
         */
        
       +#ifndef _WINDOW_H_
       +#define _WINDOW_H_
       +
       +#include <stdarg.h>
       +
        typedef struct bottom_bar bottom_bar;
        
        typedef struct {
       t@@ -175,6 +180,13 @@ char *window_get_bottom_bar_text(ledit_window *window);
        void window_show_message(ledit_window *window, char *text, int len);
        
        /*
       + * Show a non-editable message that is given as a
       + * format string and arguments as interpreted by
       + * vsnprintf(3)
       + */
       +void window_show_message_fmt(ledit_window *window, char *fmt, ...);
       +
       +/*
         * Hide the non-editable message.
         */
        void window_hide_message(ledit_window *window);
       t@@ -323,3 +335,5 @@ void window_clipboard_event(ledit_window *window, XEvent *event);
         * the position that text is being inserted at.
         */
        void xximspot(ledit_window *window, int x, int y);
       +
       +#endif