From a5a0a5561eaab9d17cc42e491b292a33f74cde21 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Wed, 16 Dec 2020 00:25:35 -0500 Subject: fixed problem with adding lines; delete key --- buffer.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- main.c | 6 ++++- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/buffer.c b/buffer.c index a466a6d..150d2a3 100644 --- a/buffer.c +++ b/buffer.c @@ -28,6 +28,25 @@ typedef struct { Line *lines; } TextBuffer; +// for debugging +#if DEBUG +static void buffer_pos_check_valid(TextBuffer *buffer, BufferPos p) { + assert(p.line < buffer->nlines); + assert(p.index <= buffer->lines[p.line].len); +} + +// perform a series of checks to make sure the buffer doesn't have any invalid values +static void buffer_check_valid(TextBuffer *buffer) { + assert(buffer->nlines); + buffer_pos_check_valid(buffer, buffer->cursor_pos); +} +#else +static void buffer_check_valid(TextBuffer *buffer) { + (void)buffer; +} +#endif + + void buffer_create(TextBuffer *buffer, Font *font) { util_zero_memory(buffer, sizeof *buffer); buffer->font = font; @@ -84,12 +103,16 @@ static void buffer_line_append_char(TextBuffer *buffer, Line *line, char32_t c) line->str[line->len++] = c; } +static void buffer_line_free(Line *line) { + free(line->str); +} + // 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); + buffer_line_free(&lines[i]); } free(lines); buffer->nlines = 0; @@ -513,6 +536,7 @@ static void buffer_insert_lines(TextBuffer *buffer, u32 where, u32 number) { (old_nlines - where) * sizeof *buffer->lines); // zero new lines util_zero_memory(buffer->lines + where, number * sizeof *buffer->lines); + buffer->nlines = new_nlines; } } @@ -525,8 +549,8 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 // `text` could consist of multiple lines, e.g. U"line 1\nline 2", // so we need to go through them one by one u32 n_added_lines = (u32)str32_count_char(str, U'\n'); - buffer_insert_lines(buffer, line_idx + 1, n_added_lines); if (n_added_lines) { + buffer_insert_lines(buffer, line_idx + 1, n_added_lines); // move any text past the cursor on this line to the last added line. Line *last_line = &buffer->lines[line_idx + n_added_lines]; u32 chars_moved = line->len - index; @@ -593,9 +617,62 @@ void buffer_insert_utf8_at_cursor(TextBuffer *buffer, char const *utf8) { } } -void buffer_delete_chars_at_cursor(TextBuffer *buffer, u32 nchars) { - // @TODO - (void)buffer, (void)nchars; +static void buffer_shorten_line(Line *line, u32 new_len) { + assert(line->len >= new_len); + line->len = new_len; // @OPTIMIZE(memory): decrease line capacity +} + +// decrease the number of lines in the buffer. +// DOES NOT DO ANYTHING TO THE LINES REMOVED! YOU NEED TO FREE THEM YOURSELF! +static void buffer_shorten(TextBuffer *buffer, u32 new_nlines) { + buffer->nlines = new_nlines; // @OPTIMIZE(memory): decrease lines capacity +} + +void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, u64 nchars) { + u32 line_idx = pos.line; + u32 index = pos.index; + Line *line = &buffer->lines[line_idx], *lines_end = &buffer->lines[buffer->nlines]; + if (nchars + index > line->len) { + // delete rest of line + buffer_shorten_line(line, index); + nchars -= line->len - index + 1; // +1 for the newline that got deleted + Line *last_line; // last line in lines deleted + for (last_line = line + 1; last_line < lines_end && nchars > last_line->len; ++last_line) { + nchars -= last_line->len+1; + } + if (last_line == lines_end) { + // @TODO: test this + // delete everything to the end of the file + for (u32 idx = line_idx + 1; idx < buffer->nlines; ++idx) { + buffer_line_free(&buffer->lines[idx]); + } + buffer_shorten(buffer, line_idx + 1); + } else { + // join last_line to line. + u32 last_line_chars_left = (u32)(last_line->len - nchars); + if (buffer_line_grow(buffer, line, line->len + last_line_chars_left)) { + memcpy(line->str + line->len, last_line->str, last_line_chars_left * sizeof(char32_t)); + line->len += last_line_chars_left; + } + // remove all lines between line + 1 and last_line (inclusive). + for (Line *l = line + 1; l <= last_line; ++l) { + buffer_line_free(l); + } + // @TODO: test with removing more than one line + memmove(line + 1, last_line + 1, (size_t)(lines_end - (last_line + 1)) * sizeof *line); + u32 lines_removed = (u32)(last_line - line); + buffer_shorten(buffer, buffer->nlines - lines_removed); + } + } else { + // just delete characters from this line + memmove(line->str + index, line->str + index + nchars, (size_t)(line->len - (nchars + index)) * sizeof(char32_t)); + line->len -= (u32)nchars; + } + +} + +void buffer_delete_chars_at_cursor(TextBuffer *buffer, u64 nchars) { + buffer_delete_chars_at_pos(buffer, buffer->cursor_pos, nchars); } bool buffer_cursor_move_left(TextBuffer *buffer) { diff --git a/main.c b/main.c index 9bc9b13..d400f80 100644 --- a/main.c +++ b/main.c @@ -69,7 +69,7 @@ int main(void) { buffer_create(&text_buffer, font); { - FILE *fp = fopen("main.c", "rb"); + FILE *fp = fopen("test.txt", "rb"); assert(fp); bool success = buffer_load_file(&text_buffer, fp); fclose(fp); @@ -122,6 +122,9 @@ int main(void) { case SDLK_TAB: buffer_insert_char_at_cursor(&text_buffer, U'\t'); break; + case SDLK_DELETE: + buffer_delete_chars_at_cursor(&text_buffer, 1); + break; } } break; case SDL_TEXTINPUT: { @@ -182,6 +185,7 @@ int main(void) { } //buffer_print_debug(&text_buffer); + buffer_check_valid(&text_buffer); SDL_GL_SwapWindow(window); } -- cgit v1.2.3