summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2023-02-07 14:06:53 +0100
committerAlbert Cervin <albert@acervin.com>2023-02-11 21:06:09 +0100
commitc2976cea9bbca465712534b7e523783e2ccc6c6e (patch)
tree6835cd4691c9c9c6f0467cc2b337da9ec5b68ec7 /src
parent8958e3f6c269965b19339f273dd806e985a71cde (diff)
downloaddged-c2976cea9bbca465712534b7e523783e2ccc6c6e.tar.gz
dged-c2976cea9bbca465712534b7e523783e2ccc6c6e.tar.xz
dged-c2976cea9bbca465712534b7e523783e2ccc6c6e.zip
Fix text to work more like GNU Emacs
This means that empty lines are not added until they have content.
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c29
-rw-r--r--src/buffer.h5
-rw-r--r--src/minibuffer.c15
-rw-r--r--src/text.c138
-rw-r--r--src/text.h1
5 files changed, 123 insertions, 65 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 6d3f3d9..3812386 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -176,7 +176,7 @@ bool movev(struct buffer *buffer, int rowdelta) {
}
// move dot `coldelta` chars
-void moveh(struct buffer *buffer, int coldelta) {
+bool moveh(struct buffer *buffer, int coldelta) {
int64_t new_col = (int64_t)buffer->dot.col + coldelta;
if (new_col > (int64_t)text_line_length(buffer->text, buffer->dot.line)) {
@@ -186,10 +186,14 @@ void moveh(struct buffer *buffer, int coldelta) {
} else if (new_col < 0) {
if (movev(buffer, -1)) {
buffer->dot.col = text_line_length(buffer->text, buffer->dot.line);
+ } else {
+ return false;
}
} else {
buffer->dot.col = new_col;
}
+
+ return true;
}
struct region {
@@ -341,8 +345,9 @@ void buffer_backward_delete_char(struct buffer *buffer) {
return;
}
- moveh(buffer, -1);
- buffer_forward_delete_char(buffer);
+ if (moveh(buffer, -1)) {
+ buffer_forward_delete_char(buffer);
+ }
}
void buffer_backward_char(struct buffer *buffer) { moveh(buffer, -1); }
@@ -741,6 +746,13 @@ void linenum_render_hook(struct text_chunk *line_data, uint32_t line,
(uint8_t *)" ", 1);
}
+void clear_empty_linenum_lines(uint32_t line, struct command_list *commands,
+ void *userdata) {
+ struct linenumdata *data = (struct linenumdata *)userdata;
+ uint32_t longest_nchars = data->longest_nchars;
+ command_list_draw_repeated(commands, 0, line, ' ', longest_nchars + 2);
+}
+
struct update_hook_result buffer_linenum_hook(struct buffer *buffer,
struct command_list *commands,
uint32_t width, uint32_t height,
@@ -773,7 +785,9 @@ struct update_hook_result buffer_linenum_hook(struct buffer *buffer,
struct update_hook_result res = {0};
res.margins.left = longest_nchars + 2;
res.line_render_hook.callback = linenum_render_hook;
+ res.line_render_hook.empty_callback = clear_empty_linenum_lines;
res.line_render_hook.userdata = &linenum_data;
+
return res;
}
@@ -845,7 +859,14 @@ void buffer_update(struct buffer *buffer, uint32_t width, uint32_t height,
uint32_t nlines = text_num_lines(buffer->text);
for (uint32_t linei = nlines - buffer->scroll.line + total_margins.top;
linei < height; ++linei) {
- command_list_draw_repeated(commands, 0, linei, ' ', total_width);
+
+ for (uint32_t hooki = 0; hooki < nlinehooks; ++hooki) {
+ struct line_render_hook *hook = &line_hooks[hooki];
+ hook->empty_callback(linei, commands, hook->userdata);
+ }
+
+ command_list_draw_repeated(commands, total_margins.left, linei, ' ',
+ total_width - total_margins.left);
}
// update the visual cursor position
diff --git a/src/buffer.h b/src/buffer.h
index 7a7fb84..afb32b1 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -23,6 +23,10 @@ struct margin {
typedef void (*line_render_cb)(struct text_chunk *line_data, uint32_t line,
struct command_list *commands, void *userdata);
+typedef void (*line_render_empty_cb)(uint32_t line,
+ struct command_list *commands,
+ void *userdata);
+
/**
* A line render hook
*
@@ -30,6 +34,7 @@ typedef void (*line_render_cb)(struct text_chunk *line_data, uint32_t line,
*/
struct line_render_hook {
line_render_cb callback;
+ line_render_empty_cb empty_callback;
void *userdata;
};
diff --git a/src/minibuffer.c b/src/minibuffer.c
index e057262..63cc5a8 100644
--- a/src/minibuffer.c
+++ b/src/minibuffer.c
@@ -17,12 +17,10 @@ static struct minibuffer {
struct keymap keymap;
} g_minibuffer = {0};
-void draw_prompt(struct text_chunk *line_data, uint32_t line,
- struct command_list *commands, void *userdata) {
+void draw_prompt(struct command_list *commands, void *userdata) {
uint32_t len = strlen(g_minibuffer.prompt);
command_list_set_index_color_fg(commands, 4);
- command_list_draw_text(commands, 0, line, (uint8_t *)g_minibuffer.prompt,
- len);
+ command_list_draw_text(commands, 0, 0, (uint8_t *)g_minibuffer.prompt, len);
command_list_reset_color(commands);
}
@@ -85,9 +83,9 @@ struct update_hook_result update(struct buffer *buffer,
}
struct update_hook_result res = {0};
- if (g_minibuffer.prompt_active) {
- res.margins.left = strlen(g_minibuffer.prompt);
- res.line_render_hook.callback = draw_prompt;
+ if (mb->prompt_active) {
+ res.margins.left = strlen(mb->prompt);
+ draw_prompt(commands, NULL);
}
return res;
@@ -150,8 +148,7 @@ void minibuffer_prompt(struct command_ctx command_ctx, const char *fmt, ...) {
}
minibuffer_clear();
- // make sure we have a line
- buffer_add_text(g_minibuffer.buffer, (uint8_t *)"", 0);
+
g_minibuffer.prompt_active = true;
g_minibuffer.prompt_command_ctx = command_ctx;
diff --git a/src/text.c b/src/text.c
index c733af2..04efcaa 100644
--- a/src/text.c
+++ b/src/text.c
@@ -1,10 +1,12 @@
#include "text.h"
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "display.h"
+#include "signal.h"
#include "utf8.h"
enum flags {
@@ -82,28 +84,66 @@ uint32_t text_byteindex_to_col(struct text *text, uint32_t line,
return byteidx_to_charidx(&text->lines[line], byteindex);
}
-void insert_at_col(struct line *line, uint32_t col, uint8_t *text, uint32_t len,
- uint32_t nchars) {
+void append_empty_lines(struct text *text, uint32_t numlines) {
+
+ if (text->nlines + numlines >= text->capacity) {
+ text->capacity += text->capacity + numlines > text->capacity * 2
+ ? numlines + 1
+ : text->capacity;
+ text->lines = realloc(text->lines, sizeof(struct line) * text->capacity);
+ }
+
+ for (uint32_t i = 0; i < numlines; ++i) {
+ struct line *nline = &text->lines[text->nlines];
+ nline->data = NULL;
+ nline->nbytes = 0;
+ nline->nchars = 0;
+ nline->flags = 0;
+
+ ++text->nlines;
+ }
+
+ if (text->nlines > text->capacity) {
+ printf("text->nlines: %d, text->capacity: %d\n", text->nlines,
+ text->capacity);
+ raise(SIGTRAP);
+ }
+}
+
+void ensure_line(struct text *text, uint32_t line) {
+ if (line >= text->nlines) {
+ append_empty_lines(text, line - text->nlines + 1);
+ }
+}
+
+// It is assumed that `data` does not contain any \n, that is handled by
+// higher-level functions
+void insert_at(struct text *text, uint32_t line, uint32_t col, uint8_t *data,
+ uint32_t len, uint32_t nchars) {
if (len == 0) {
return;
}
- line->nbytes += len;
- line->nchars += nchars;
- line->flags = LineChanged;
- line->data = realloc(line->data, line->nbytes);
+ ensure_line(text, line);
+
+ struct line *l = &text->lines[line];
- uint32_t bytei = charidx_to_byteidx(line, col);
+ l->nbytes += len;
+ l->nchars += nchars;
+ l->flags = LineChanged;
+ l->data = realloc(l->data, l->nbytes);
+
+ uint32_t bytei = charidx_to_byteidx(l, col);
// move following bytes out of the way
- if (bytei + len < line->nbytes) {
+ if (bytei + len < l->nbytes) {
uint32_t start = bytei + len;
- memmove(line->data + start, line->data + bytei, line->nbytes - start);
+ memmove(l->data + start, l->data + bytei, l->nbytes - start);
}
// insert new chars
- memcpy(line->data + bytei, text, len);
+ memcpy(l->data + bytei, data, len);
}
uint32_t text_line_length(struct text *text, uint32_t lineidx) {
@@ -164,35 +204,22 @@ void shift_lines(struct text *text, uint32_t start, int32_t direction) {
memmove(dest, src, nlines * sizeof(struct line));
}
-void append_empty_lines(struct text *text, uint32_t numlines) {
-
- for (uint32_t i = 0; i < numlines; ++i) {
- struct line *nline = &text->lines[text->nlines];
- nline->data = NULL;
- nline->nbytes = 0;
- nline->nchars = 0;
- nline->flags = 0;
-
- ++text->nlines;
- }
-}
-
void new_line_at(struct text *text, uint32_t line, uint32_t col) {
- if (text->nlines == text->capacity) {
- text->capacity *= 2;
- text->lines = realloc(text->lines, sizeof(struct line) * text->capacity);
- }
+ ensure_line(text, line);
+
+ uint32_t newline = line + 1;
+ bool has_newline = col < text->lines[line].nchars || newline < text->nlines;
+ append_empty_lines(text, has_newline ? 1 : 0);
- append_empty_lines(text, 1);
mark_lines_changed(text, line, text->nlines - line);
- // move following lines out of the way
- shift_lines(text, line + 1, 1);
+ // move following lines out of the way, if there are any
+ if (newline + 1 < text->nlines) {
+ shift_lines(text, newline, 1);
+ }
// split line if needed
- struct line *pl = &text->lines[line];
- struct line *cl = &text->lines[line + 1];
- split_line(col, pl, cl);
+ split_line(col, &text->lines[line], &text->lines[newline]);
}
void delete_line(struct text *text, uint32_t line) {
@@ -201,10 +228,11 @@ void delete_line(struct text *text, uint32_t line) {
}
mark_lines_changed(text, line, text->nlines - line);
+
free(text->lines[line].data);
text->lines[line].data = NULL;
- if (text->nlines > 1) {
+ if (line + 1 < text->nlines) {
shift_lines(text, line + 1, -1);
}
@@ -226,24 +254,23 @@ void text_insert_at(struct text *text, uint32_t line, uint32_t col,
uint8_t *bytes, uint32_t nbytes, uint32_t *lines_added,
uint32_t *cols_added) {
uint32_t linelen = 0, start_line = line;
- if (start_line >= text->nlines) {
- append_empty_lines(text, start_line - text->nlines + 1);
- }
- *cols_added = 0;
+ *cols_added = 0;
for (uint32_t bytei = 0; bytei < nbytes; ++bytei) {
uint8_t byte = bytes[bytei];
if (byte == '\n') {
uint8_t *line_data = bytes + (bytei - linelen);
uint32_t nchars = utf8_nchars(line_data, linelen);
- insert_at_col(&text->lines[line], col, line_data, linelen, nchars);
- col += nchars;
- new_line_at(text, line, col);
- ++line;
+ insert_at(text, line, col, line_data, linelen, nchars);
+
+ if (linelen == 0) {
+ new_line_at(text, line, col);
+ }
- col = text_line_length(text, line);
+ ++line;
linelen = 0;
+ col = 0;
} else {
++linelen;
}
@@ -253,7 +280,7 @@ void text_insert_at(struct text *text, uint32_t line, uint32_t col,
if (linelen > 0) {
uint8_t *line_data = bytes + (nbytes - linelen);
uint32_t nchars = utf8_nchars(line_data, linelen);
- insert_at_col(&text->lines[line], col, line_data, linelen, nchars);
+ insert_at(text, line, col, line_data, linelen, nchars);
*cols_added = nchars;
}
@@ -285,8 +312,8 @@ void text_delete(struct text *text, uint32_t start_line, uint32_t start_col,
lastline->nbytes - bytei);
} else {
// otherwise we actually have to copy from the last line
- insert_at_col(firstline, start_col, lastline->data + bytei,
- lastline->nbytes - bytei, lastline->nchars - end_col);
+ insert_at(text, start_line, start_col, lastline->data + bytei,
+ lastline->nbytes - bytei, lastline->nchars - end_col);
}
firstline->nchars = start_col + (lastline->nchars - end_col);
@@ -299,6 +326,11 @@ void text_delete(struct text *text, uint32_t start_line, uint32_t start_col,
linei > start_line; --linei) {
delete_line(text, linei);
}
+
+ // if this is the last line in the buffer, and it turns out empty, remove it
+ if (firstline->nbytes == 0 && start_line == text->nlines - 1) {
+ delete_line(text, start_line);
+ }
}
void text_for_each_chunk(struct text *text, chunk_cb callback, void *userdata) {
@@ -334,7 +366,7 @@ struct text_chunk text_get_line(struct text *text, uint32_t line) {
struct copy_cmd {
uint32_t line;
- uint32_t byteindex;
+ uint32_t byteoffset;
uint32_t nbytes;
};
@@ -352,7 +384,7 @@ struct text_chunk text_get_region(struct text *text, uint32_t start_line,
return (struct text_chunk){0};
}
- // handle deletion of newlines
+ // handle copying of newlines
if (end_col > last_line->nchars) {
++end_line;
end_col = 0;
@@ -370,7 +402,7 @@ struct text_chunk text_get_region(struct text *text, uint32_t start_line,
struct copy_cmd *cmd = &copy_cmds[line - start_line];
cmd->line = line;
- cmd->byteindex = 0;
+ cmd->byteoffset = 0;
cmd->nbytes = l->nbytes;
}
@@ -378,7 +410,7 @@ struct text_chunk text_get_region(struct text *text, uint32_t start_line,
struct copy_cmd *cmd_first = &copy_cmds[0];
uint32_t byteoff =
utf8_nbytes(first_line->data, first_line->nbytes, start_col);
- cmd_first->byteindex += byteoff;
+ cmd_first->byteoffset += byteoff;
cmd_first->nbytes -= byteoff;
total_bytes -= byteoff;
total_chars -= start_col;
@@ -390,13 +422,14 @@ struct text_chunk text_get_region(struct text *text, uint32_t start_line,
total_bytes -= (last_line->nbytes - byteindex);
total_chars -= (last_line->nchars - end_col);
- uint8_t *data = (uint8_t *)malloc(total_bytes + end_line - start_line);
+ uint8_t *data = (uint8_t *)malloc(
+ total_bytes + /* nr of newline chars */ (end_line - start_line));
// copy data
for (uint32_t cmdi = 0, curr = 0; cmdi < nlines; ++cmdi) {
struct copy_cmd *c = &copy_cmds[cmdi];
struct line *l = &text->lines[c->line];
- memcpy(data + curr, l->data + c->byteindex, c->nbytes);
+ memcpy(data + curr, l->data + c->byteoffset, c->nbytes);
curr += c->nbytes;
if (cmdi != (nlines - 1)) {
@@ -413,6 +446,7 @@ struct text_chunk text_get_region(struct text *text, uint32_t start_line,
.line = 0,
.nbytes = total_bytes,
.nchars = total_chars,
+ .allocated = true,
};
}
diff --git a/src/text.h b/src/text.h
index 12fe576..fbee89b 100644
--- a/src/text.h
+++ b/src/text.h
@@ -37,6 +37,7 @@ struct text_chunk {
uint32_t nbytes;
uint32_t nchars;
uint32_t line;
+ bool allocated;
};
typedef void (*chunk_cb)(struct text_chunk *chunk, void *userdata);