summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.h1
-rw-r--r--buffer.c119
-rw-r--r--main.c8
-rw-r--r--string32.c50
-rw-r--r--util.c19
5 files changed, 170 insertions, 27 deletions
diff --git a/base.h b/base.h
index 114563f..65c8f20 100644
--- a/base.h
+++ b/base.h
@@ -17,6 +17,7 @@
#include <float.h>
#include <limits.h>
#include <assert.h>
+#include <uchar.h>
typedef uint8_t u8;
typedef uint16_t u16;
diff --git a/buffer.c b/buffer.c
index 42c5ee3..47fd1ba 100644
--- a/buffer.c
+++ b/buffer.c
@@ -2,6 +2,7 @@
#include "unicode.h"
#include "util.c"
#include "text.h"
+#include "string32.c"
// a position in the buffer
typedef struct {
@@ -11,6 +12,7 @@ typedef struct {
typedef struct {
u32 len;
+ u32 capacity;
char32_t *str;
} Line;
@@ -19,6 +21,7 @@ typedef struct {
Font *font;
BufferPos cursor_pos;
u8 tab_width;
+ bool out_of_mem; // set to true if an allocation fails
float x1, y1, x2, y2;
u32 nlines;
Line *lines;
@@ -30,19 +33,49 @@ void buffer_create(TextBuffer *buffer, Font *font) {
buffer->tab_width = 4;
}
-static Status buffer_line_append_char(Line *line, char32_t c) {
- if (line->len == 0 || util_is_power_of_2(line->len)) {
- // when the length of a line gets to a power of 2, double its allocated size
- size_t new_size = line->len == 0 ? 1 : line->len * 2;
- line->str = realloc(line->str, new_size * sizeof *line->str);
- if (!line->str) {
+// grow capacity of line to at least minimum_capacity
+// returns true if allocation was succesful
+static Status buffer_line_grow(TextBuffer *buffer, Line *line, u32 minimum_capacity) {
+ while (line->capacity < minimum_capacity) {
+ // double capacity of line
+ u32 new_capacity = line->capacity == 0 ? 4 : line->capacity * 2;
+ if (new_capacity < line->capacity) {
+ // overflow. this could only happen with an extremely long line, so we
+ // treat it as
+ buffer->out_of_mem = true;
return false;
}
+ char32_t *new_str = realloc(line->str, new_capacity * sizeof *line->str);
+ if (!new_str) {
+ // allocation failed ):
+ buffer->out_of_mem = true;
+ return false;
+ }
+ // allocation successful
+ line->str = new_str;
+ line->capacity = new_capacity;
}
- line->str[line->len++] = c;
return true;
}
+static void buffer_line_append_char(TextBuffer *buffer, Line *line, char32_t c) {
+ if (buffer_line_grow(buffer, line, line->len + 1))
+ line->str[line->len++] = c;
+}
+
+// Does not free the pointer `buffer` (buffer might not have even been allocated with malloc)
+void buffer_free(TextBuffer *buffer) {
+ Line *lines = buffer->lines;
+ u32 nlines = buffer->nlines;
+ for (u32 i = 0; i < nlines; ++i) {
+ free(lines[i].str);
+ }
+ free(lines);
+ buffer->nlines = 0;
+ buffer->lines = NULL;
+}
+
+
// fp should be a binary file
Status buffer_load_file(TextBuffer *buffer, FILE *fp) {
assert(fp);
@@ -97,10 +130,7 @@ Status buffer_load_file(TextBuffer *buffer, FILE *fp) {
u32 line_idx = buffer->nlines - 1;
Line *line = &buffer->lines[line_idx];
- if (!buffer_line_append_char(line, c)) {
- success = false;
- break;
- }
+ buffer_line_append_char(buffer, line, c);
}
}
}
@@ -108,10 +138,7 @@ Status buffer_load_file(TextBuffer *buffer, FILE *fp) {
}
if (ferror(fp)) success = false;
if (!success) {
- for (u32 i = 0; i < buffer->nlines; ++i)
- free(buffer->lines[i].str);
- free(buffer->lines);
- buffer->nlines = 0;
+ buffer_free(buffer);
}
return success;
@@ -134,18 +161,6 @@ static void buffer_print(TextBuffer const *buffer) {
fflush(stdout);
}
-// Does not free the pointer `buffer` (buffer might not have even been allocated with malloc)
-void buffer_free(TextBuffer *buffer) {
- Line *lines = buffer->lines;
- u32 nlines = buffer->nlines;
- for (u32 i = 0; i < nlines; ++i) {
- free(lines[i].str);
- }
- free(lines);
- buffer->nlines = 0;
- buffer->lines = NULL;
-}
-
static u32 buffer_index_to_column(TextBuffer *buffer, u32 line, u32 index) {
char32_t *str = buffer->lines[line].str;
u32 col = 0;
@@ -469,6 +484,56 @@ static void buffer_scroll_to_cursor(TextBuffer *buffer) {
buffer_correct_scroll(buffer); // it's possible that min/max_scroll_x/y go too far
}
+void buffer_insert_text_at_cursor(TextBuffer *buffer, char32_t const *text, size_t len) {
+ u32 cur_line_idx = buffer->cursor_pos.line;
+ u32 cur_index = buffer->cursor_pos.index;
+ char32_t const *end = text + len;
+
+ // `text` could consist of multiple lines, e.g. U"line 1\nline 2",
+ // so we need to go through them one by one
+ while (text != end) {
+ char32_t const *end_of_line = util_mem32chr_const(text, U'\n', len);
+ if (!end_of_line) end_of_line = end;
+
+ u32 text_line_len = (u32)(end_of_line - text);
+ Line *cur_line = &buffer->lines[cur_line_idx];
+ u32 old_len = cur_line->len;
+ u32 new_len = old_len + text_line_len;
+ if (new_len > old_len) { // handles both overflow and empty text lines
+ if (buffer_line_grow(buffer, cur_line, new_len)) {
+ // make space for text
+ memmove(cur_line->str + cur_index + (new_len - old_len),
+ cur_line->str + cur_index,
+ (old_len - cur_index) * sizeof *text);
+ // insert text
+ memcpy(cur_line->str + cur_index, text, text_line_len * sizeof *text);
+
+ cur_line->len = new_len;
+ }
+
+ text += text_line_len;
+ len -= text_line_len;
+ cur_index = buffer->cursor_pos.index += text_line_len;
+ }
+
+ // @TODO: deal with multiple lines
+ }
+
+ buffer_scroll_to_cursor(buffer);
+}
+
+void buffer_insert_char_at_cursor(TextBuffer *buffer, char32_t c) {
+ buffer_insert_text_at_cursor(buffer, &c, 1);
+}
+
+void buffer_insert_utf8_at_cursor(TextBuffer *buffer, char const *utf8) {
+ String32 s32 = s32_from_utf8(utf8);
+ if (s32.str) {
+ buffer_insert_text_at_cursor(buffer, s32.str, s32.len);
+ s32_free(&s32);
+ }
+}
+
bool buffer_cursor_move_left(TextBuffer *buffer) {
if (buffer_pos_move_left(buffer, &buffer->cursor_pos)) {
buffer_scroll_to_cursor(buffer);
diff --git a/main.c b/main.c
index ac5fcc4..6d9e046 100644
--- a/main.c
+++ b/main.c
@@ -116,8 +116,16 @@ int main(void) {
case SDLK_DOWN:
buffer_cursor_move_down(&text_buffer);
break;
+ case SDLK_RETURN:
+ //buffer_insert_text_at_cursor(buffer, U"\n", 1);
+ break;
}
} break;
+ case SDL_TEXTINPUT: {
+ char *text = event.text.text;
+ printf("TEXT: %s\n",text);
+ buffer_insert_utf8_at_cursor(&text_buffer, text);
+ } break;
}
}
diff --git a/string32.c b/string32.c
new file mode 100644
index 0000000..9b14e30
--- /dev/null
+++ b/string32.c
@@ -0,0 +1,50 @@
+// UTF-32 string
+typedef struct {
+ size_t len;
+ char32_t *str;
+} String32;
+
+void s32_free(String32 *s) {
+ free(s->str);
+ s->str = NULL;
+ s->len = 0;
+}
+
+// the string returned should be s32_free'd.
+// this will return an empty string if the allocation failed or the string is invalid UTF-8
+String32 s32_from_utf8(char const *utf8) {
+ String32 string = {0, NULL};
+ size_t len = strlen(utf8);
+ if (len) {
+ // the wide string uses at most as many "characters" (elements?) as the UTF-8 string
+ char32_t *widestr = calloc(len, sizeof *widestr);
+ if (widestr) {
+ char32_t *wide_p = widestr;
+ char const *utf8_p = utf8;
+ char const *utf8_end = utf8_p + len;
+ mbstate_t mbstate = {0};
+ while (utf8_p < utf8_end) {
+ char32_t c = 0;
+ size_t n = mbrtoc32(&c, utf8_p, (size_t)(utf8_end - utf8_p), &mbstate);
+ if (n == 0// null character. this shouldn't happen.
+ || n == (size_t)(-2) // incomplete character
+ || n == (size_t)(-1) // invalid UTF-8
+ ) {
+ free(widestr);
+ widestr = wide_p = NULL;
+ break;
+ } else if (n == (size_t)(-3)) { // no bytes consumed, but a character was produced
+ *wide_p++ = c;
+ } else {
+ // n bytes consumed
+ *wide_p++ = c;
+ utf8_p += n;
+ }
+ }
+ string.str = widestr;
+ string.len = (size_t)(wide_p - widestr);
+ }
+ }
+ return string;
+}
+
diff --git a/util.c b/util.c
index e183461..09da480 100644
--- a/util.c
+++ b/util.c
@@ -33,4 +33,23 @@ static double util_mind(double a, double b) {
return a < b ? a : b;
}
+// for finding a character in a char32 string
+static char32_t *util_mem32chr(char32_t *s, char32_t c, size_t n) {
+ for (size_t i = 0; i < n; ++i) {
+ if (s[i] == c) {
+ return &s[i];
+ }
+ }
+ return NULL;
+}
+
+static char32_t const *util_mem32chr_const(char32_t const *s, char32_t c, size_t n) {
+ for (size_t i = 0; i < n; ++i) {
+ if (s[i] == c) {
+ return &s[i];
+ }
+ }
+ return NULL;
+}
+
#endif