Introduction
Introduction Statistics Contact Development Disclaimer Help
tImprove file handling - 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 22ffb413e9e0be6a5764de4bcc951e6a0480bad3
parent b8557a969d0da663fe3175d19269cb2f37eb9e72
Author: lumidify <[email protected]>
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(-)
---
diff --git a/buffer.c b/buffer.c
t@@ -191,8 +191,8 @@ buffer_insert_mark(ledit_buffer *buffer, char *mark, size_…
}
if (marklist->len == marklist->alloc) {
size_t new_alloc = marklist->alloc > 0 ? marklist->alloc * 2 :…
- 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, si…
}
}
-/* 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 **err…
t@@ -355,6 +356,7 @@ buffer_load_file(ledit_buffer *buffer, char *filename, siz…
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, siz…
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, siz…
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, cha…
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, si…
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, si…
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
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 buffe…
size_t lines_num; /* number of lines */
+ int modified; /* whether buffer was modified since …
int hard_line_based; /* whether operations should work on …
Note that this doesn't actually ch…
the buffer functions, it is just s…
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 :…
- 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…
- 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…
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, l…
+ 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;
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…
static int parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret…
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,…
+ if (filename) {
+ struct stat sb;
+ /* There technically is a race between checking stat and actua…
+ 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 over…
+ filename
+ );
+ /* FIXME: I guess the file can still exist if stat returns an …
+ 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, &e…
+ window_show_message_fmt(view->window, "Error writing %…
+ } else {
+ /* FIXME: better message */
+ window_show_message_fmt(view->window, "Wrote file %s",…
+ }
+ } 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…
+ } else {
+ ledit_cleanup();
+ exit(0);
+ }
return 0;
}
t@@ -161,11 +209,9 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t…
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
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[…
- 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…
+ } else if (readonly) {
+ window_show_message_fmt(view->window, "%s: rea…
+ } 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();
}
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";
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…
+ 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, w…
+ }
+}
+
+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-…
+ pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &wi…
+ draw_grow(window, window->bb->line_draw, window->bb->line_w, window->b…
+ XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0…
+ pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme…
+ 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 …
if (len == -1)
len = strlen(text);
/* \0 not included in len */
- if (window->bb->line_len + len + 1 > window->bb->line_alloc || window-…
- /* FIXME: read up on what the best values are here */
- window->bb->line_alloc =
- window->bb->line_alloc * 2 > window->bb->line_len + len + …
- window->bb->line_alloc * 2 :
- window->bb->line_len + len + 1;
- window->bb->line_text = ledit_realloc(window->bb->line_text, w…
- }
+ 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 …
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-…
- pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &wi…
- draw_grow(window, window->bb->line_draw, window->bb->line_w, window->b…
- XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0…
- pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme…
- 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…
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…
+ 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, …
+ }
+ 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, e…
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_SCA…
+ redraw_line_text(window);
+ } else {
+ recalc_text_size(window);
+ }
if (window->resize_callback)
window->resize_callback(window->resize_cb_data);
window->redraw = 1;
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 …
*/
+#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 *…
* the position that text is being inserted at.
*/
void xximspot(ledit_window *window, int x, int y);
+
+#endif
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.