summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2022-11-18 10:55:49 +0100
committerAlbert Cervin <albert@acervin.com>2022-11-25 23:09:13 +0100
commitc40a1cfa0f1ecfddac06c7143c2dd974126ddad3 (patch)
tree95410887b40166226e2c86001eb923cc51d8373d
parent2f4cb88d5c60f725323739300bb49dfa8923e7d5 (diff)
downloaddged-c40a1cfa0f1ecfddac06c7143c2dd974126ddad3.tar.gz
dged-c40a1cfa0f1ecfddac06c7143c2dd974126ddad3.tar.xz
dged-c40a1cfa0f1ecfddac06c7143c2dd974126ddad3.zip
Rework delete logic a bit
It now handles a range of characters and correctly merges lines. It is not the most slick implementation but it works as a start.
-rw-r--r--src/buffer.c11
-rw-r--r--src/buffer.h1
-rw-r--r--src/text.c122
-rw-r--r--src/text.h11
-rw-r--r--test/text.c40
5 files changed, 129 insertions, 56 deletions
diff --git a/src/buffer.c b/src/buffer.c
index e08bca5..fb071da 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -98,14 +98,13 @@ void moveh(struct buffer *buffer, int coldelta) {
}
}
+void buffer_forward_delete_char(struct buffer *buffer) {
+ text_delete(buffer->text, buffer->dot_line, buffer->dot_col, 1);
+}
+
void buffer_backward_delete_char(struct buffer *buffer) {
- // TODO: merge lines
- if (text_line_length(buffer->text, buffer->dot_line) == 0) {
- text_delete_line(buffer->text, buffer->dot_line);
- } else if (buffer->dot_col > 0) {
- text_delete(buffer->text, buffer->dot_line, buffer->dot_col - 1, 1);
- }
moveh(buffer, -1);
+ buffer_forward_delete_char(buffer);
}
void buffer_backward_char(struct buffer *buffer) { moveh(buffer, -1); }
diff --git a/src/buffer.h b/src/buffer.h
index 1b73505..66096b9 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -40,6 +40,7 @@ void buffer_add_keymap(struct buffer *buffer, struct keymap *keymap);
int buffer_add_text(struct buffer *buffer, uint8_t *text, uint32_t nbytes);
+void buffer_forward_delete_char(struct buffer *buffer);
void buffer_backward_delete_char(struct buffer *buffer);
void buffer_backward_char(struct buffer *buffer);
void buffer_forward_char(struct buffer *buffer);
diff --git a/src/text.c b/src/text.c
index 4162e94..64cbd7b 100644
--- a/src/text.c
+++ b/src/text.c
@@ -48,6 +48,28 @@ void text_destroy(struct text *text) {
free(text->lines);
}
+// given `char_idx` as a character index, return the byte index
+uint32_t charidx_to_byteidx(struct line *line, uint32_t char_idx) {
+ if (char_idx > line->nchars) {
+ return line->nbytes;
+ }
+ return utf8_nbytes(line->data, char_idx);
+}
+
+// TODO: grapheme clusters
+// given `byte_idx` as a byte index, return the character index
+uint32_t byteidx_to_charidx(struct line *line, uint32_t byte_idx) {
+ if (byte_idx > line->nbytes) {
+ return line->nchars;
+ }
+
+ return utf8_nchars(line->data, byte_idx);
+}
+
+uint32_t char_byte_size(struct line *line, uint32_t byte_idx) {
+ return utf8_nbytes(line->data + byte_idx, 1);
+}
+
void append_to_line(struct line *line, uint32_t col, uint8_t *text,
uint32_t len, uint32_t nchars) {
@@ -58,13 +80,19 @@ void append_to_line(struct line *line, uint32_t col, uint8_t *text,
line->nbytes += len;
line->nchars += nchars;
line->flags = LineChanged;
- line->data = realloc(line->data, line->nbytes + len);
+ line->data = realloc(line->data, line->nbytes);
+
+ uint32_t bytei = charidx_to_byteidx(line, col);
// move chars out of the way
- memmove(line->data + col + 1, line->data + col, line->nbytes - (col + 1));
+ if (col + nchars < line->nchars) {
+ uint32_t nextcbytei = charidx_to_byteidx(line, col + nchars);
+ memmove(line->data + nextcbytei, line->data + bytei,
+ line->nbytes - nextcbytei);
+ }
// insert new chars
- memcpy(line->data + col, text, len);
+ memcpy(line->data + bytei, text, len);
}
uint32_t text_line_length(struct text *text, uint32_t lineidx) {
@@ -77,28 +105,6 @@ uint32_t text_line_size(struct text *text, uint32_t lineidx) {
uint32_t text_num_lines(struct text *text) { return text->nlines; }
-// given `char_idx` as a character index, return the byte index
-uint32_t charidx_to_byteidx(struct line *line, uint32_t char_idx) {
- if (char_idx > line->nchars) {
- return line->nchars;
- }
- return utf8_nbytes(line->data, char_idx);
-}
-
-// TODO: grapheme clusters
-// given `byte_idx` as a byte index, return the character index
-uint32_t byteidx_to_charidx(struct line *line, uint32_t byte_idx) {
- if (byte_idx > line->nbytes) {
- return line->nchars;
- }
-
- return utf8_nchars(line->data, byte_idx);
-}
-
-uint32_t char_byte_size(struct line *line, uint32_t byte_idx) {
- return utf8_nbytes(line->data + byte_idx, 1);
-}
-
void split_line(uint32_t col, struct line *line, struct line *next) {
uint8_t *data = line->data;
uint32_t nbytes = line->nbytes;
@@ -169,7 +175,7 @@ void new_line_at(struct text *text, uint32_t line, uint32_t col) {
split_line(col, pl, cl);
}
-void text_delete_line(struct text *text, uint32_t line) {
+void delete_line(struct text *text, uint32_t line) {
// always keep a single line
if (text->nlines == 1) {
return;
@@ -230,20 +236,49 @@ void text_append(struct text *text, uint32_t line, uint32_t col, uint8_t *bytes,
void text_delete(struct text *text, uint32_t line, uint32_t col,
uint32_t nchars) {
- struct line *lp = &text->lines[line];
- uint32_t max_chars = nchars > lp->nchars ? lp->nchars : nchars;
- if (lp->nchars > 0) {
- // get byte index and size for char to remove
- uint32_t bytei = charidx_to_byteidx(lp, col);
- uint32_t nbytes = utf8_nbytes(lp->data + bytei, max_chars);
+ // delete chars from current line
+ struct line *lp = &text->lines[line];
+ uint32_t chars_initial_line =
+ col + nchars > lp->nchars ? (lp->nchars - col) : nchars;
+ uint32_t bytei = charidx_to_byteidx(lp, col);
+ uint32_t nbytes = utf8_nbytes(lp->data + bytei, chars_initial_line);
+
+ memcpy(lp->data + bytei, lp->data + bytei + nbytes,
+ lp->nbytes - (bytei + nbytes));
+
+ lp->nbytes -= nbytes;
+ lp->nchars -= chars_initial_line;
+ lp->flags |= LineChanged;
+
+ uint32_t initial_line = line;
+ uint32_t left_to_delete = nchars - chars_initial_line;
+
+ // grab remaining chars from last line to delete from (if any)
+ uint32_t src_col = 0;
+ while (left_to_delete > 0 && line < text->nlines) {
+ ++line;
+ --left_to_delete; // newline char
+
+ struct line *lp = &text->lines[line];
+ uint32_t deleted_in_line =
+ left_to_delete > lp->nchars ? lp->nchars : left_to_delete;
+ src_col = deleted_in_line;
+ left_to_delete -= deleted_in_line;
+ }
- memcpy(lp->data + bytei, lp->data + bytei + nbytes,
- lp->nbytes - (bytei + nbytes));
+ if (line != initial_line) {
+ struct line *lp = &text->lines[line];
+ uint32_t bytei = charidx_to_byteidx(lp, src_col);
+ if (src_col < lp->nchars) {
+ append_to_line(&text->lines[initial_line], col, lp->data + bytei,
+ lp->nbytes - bytei, lp->nchars - src_col);
+ }
+ }
- lp->nbytes -= nbytes;
- lp->nchars -= max_chars;
- lp->flags |= LineChanged;
+ // delete all lines from current line + 1 to (and including) last line
+ for (uint32_t li = initial_line + 1; li <= line; ++li) {
+ delete_line(text, li);
}
}
@@ -272,11 +307,16 @@ uint32_t text_render(struct text *text, uint32_t line, uint32_t nlines,
return ncmds;
}
+void text_for_each_chunk(struct text *text, chunk_cb callback) {
+ // if representation of text is changed, this can be changed as well
+ text_for_each_line(text, 0, text->nlines, callback);
+}
+
void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
- line_cb callback) {
+ chunk_cb callback) {
for (uint32_t li = line; li < (line + nlines); ++li) {
struct line *src_line = &text->lines[li];
- struct txt_line line = (struct txt_line){
+ struct text_chunk line = (struct text_chunk){
.text = src_line->data,
.nbytes = src_line->nbytes,
.nchars = src_line->nchars,
@@ -285,9 +325,9 @@ void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
}
}
-struct txt_line text_get_line(struct text *text, uint32_t line) {
+struct text_chunk text_get_line(struct text *text, uint32_t line) {
struct line *src_line = &text->lines[line];
- return (struct txt_line){
+ return (struct text_chunk){
.text = src_line->data,
.nbytes = src_line->nbytes,
.nchars = src_line->nchars,
diff --git a/src/text.h b/src/text.h
index 21de899..399eaf4 100644
--- a/src/text.h
+++ b/src/text.h
@@ -15,7 +15,6 @@ void text_append(struct text *text, uint32_t line, uint32_t col, uint8_t *bytes,
void text_delete(struct text *text, uint32_t line, uint32_t col,
uint32_t nchars);
-void text_delete_line(struct text *text, uint32_t line);
uint32_t text_render(struct text *text, uint32_t line, uint32_t nlines,
struct render_cmd *cmds, uint32_t max_ncmds);
@@ -24,16 +23,18 @@ uint32_t text_num_lines(struct text *text);
uint32_t text_line_length(struct text *text, uint32_t lineidx);
uint32_t text_line_size(struct text *text, uint32_t lineidx);
-struct txt_line {
+struct text_chunk {
uint8_t *text;
uint32_t nbytes;
uint32_t nchars;
};
-typedef void (*line_cb)(struct txt_line *line);
+typedef void (*chunk_cb)(struct text_chunk *chunk);
void text_for_each_line(struct text *text, uint32_t line, uint32_t nlines,
- line_cb callback);
+ chunk_cb callback);
-struct txt_line text_get_line(struct text *text, uint32_t line);
+void text_for_each_chunk(struct text *text, chunk_cb callback);
+
+struct text_chunk text_get_line(struct text *text, uint32_t line);
bool text_line_contains_unicode(struct text *text, uint32_t line);
diff --git a/test/text.c b/test/text.c
index ec99890..9073553 100644
--- a/test/text.c
+++ b/test/text.c
@@ -1,4 +1,5 @@
#include "assert.h"
+#include "stdio.h"
#include "test.h"
#include "text.h"
@@ -7,7 +8,7 @@
#include <string.h>
#include <wchar.h>
-void assert_line_equal(struct txt_line *line) {}
+void assert_line_equal(struct text_chunk *line) {}
void test_add_text() {
uint32_t lines_added, cols_added;
@@ -25,8 +26,12 @@ void test_add_text() {
const char *txt2 = "This is line 2\n";
text_append(t, 1, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
&cols_added);
+ ASSERT(text_num_lines(t) == 3,
+ "Expected text to have three lines after second insertion");
ASSERT_STR_EQ((const char *)text_get_line(t, 1).text, "This is line 2",
"Expected line 2 to be line 2");
+
+ text_destroy(t);
}
void test_delete_text() {
@@ -49,13 +54,36 @@ void test_delete_text() {
const char *txt2 = "This is line 1\nThis is line 2\nThis is line 3";
text_append(t, 0, 0, (uint8_t *)txt2, strlen(txt2), &lines_added,
&cols_added);
+ ASSERT(text_num_lines(t) == 3,
+ "Expected to have three lines after inserting as many");
+
text_delete(t, 1, 11, 3);
ASSERT(text_line_length(t, 1) == 11,
"Expected line to contain 11 chars after deletion");
- struct txt_line line = text_get_line(t, 1);
+ struct text_chunk line = text_get_line(t, 1);
ASSERT(strncmp((const char *)line.text, "This is lin", line.nbytes) == 0,
"Expected deleted characters to be gone in the second line");
+ text_delete(t, 1, 0, text_line_length(t, 1) + 1);
+ ASSERT(text_num_lines(t) == 2,
+ "Expected to have two lines after deleting one");
+ struct text_chunk line2 = text_get_line(t, 1);
+ ASSERT(strncmp((const char *)line2.text, "This is line 3", line2.nbytes) == 0,
+ "Expected lines to have shifted upwards after deleting");
+
+ struct text *t3 = text_create(10);
+ const char *delete_me = "This is line๐ŸŽ™\nQ";
+ text_append(t3, 0, 0, (uint8_t *)delete_me, strlen(delete_me), &lines_added,
+ &cols_added);
+ text_delete(t3, 0, 13, 1);
+ struct text_chunk top_line = text_get_line(t3, 0);
+ ASSERT(strncmp((const char *)top_line.text, "This is line๐ŸŽ™Q",
+ top_line.nbytes) == 0,
+ "Expected text from second line to be appended to first line when "
+ "deleting newline");
+ ASSERT(text_num_lines(t3) == 1,
+ "Expected text to have one line after deleting newline");
+
// test utf-8
struct text *t2 = text_create(10);
const char *txt3 = "Emojis: ๐Ÿ‡ซ๐Ÿ‡ฎ ๐Ÿฎ\n";
@@ -71,9 +99,13 @@ void test_delete_text() {
text_delete(t2, 0, 10, 2);
ASSERT(text_line_length(t2, 0) == 10,
"Line length should be 10 after deleting the cow emoji and a space");
- struct txt_line line2 = text_get_line(t2, 0);
- ASSERT(strncmp((const char *)line2.text, "Emojis: ๐Ÿ‡ซ๐Ÿ‡ฎ", line2.nbytes) == 0,
+ struct text_chunk line3 = text_get_line(t2, 0);
+ ASSERT(strncmp((const char *)line3.text, "Emojis: ๐Ÿ‡ซ๐Ÿ‡ฎ", line3.nbytes) == 0,
"Expected cow emoji plus space to be deleted");
+
+ text_destroy(t);
+ text_destroy(t2);
+ text_destroy(t3);
}
void run_text_tests() {