diff --git a/.gitignore b/.gitignore index c30cc64..bfff791 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ kilo .cache .clangd +.nvim.lua compile_commands.json -test_file diff --git a/include/buffer.h b/include/buffer.h index 03656a5..c588c02 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -5,34 +5,27 @@ #include #include "utils.h" - -struct erow { - char *chars; - int n_chars; - - char *rchars; - int n_rchars; -}; +#include "erow.h" struct buffer { char *filename; - struct erow *rows; + + int cx, cy, rx; + int row_off, col_off; + + struct erow **rows; int n_rows; + bool modified; }; struct buffer *buffer_create(void); -void buffer_read_file(struct buffer *buffer, const char *filename); -void buffer_insert_row(struct buffer *buffer, const char *chars, int n_chars, int at); +ERRCODE buffer_read_file(struct buffer *buffer, const char *filename); +ERRCODE buffer_write_file(struct buffer *buffer, size_t *bytes_written); +void buffer_insert_row(struct buffer *buffer, struct erow *erow, int at); void buffer_delete_row(struct buffer *buffer, int at); -ERRCODE buffer_write_file(struct buffer *buffer, int *bytes_written); +struct erow *buffer_get_crow(struct buffer *buffer); +void buffer_free(struct buffer *buffer); -void erow_update_rendering(struct erow *erow); -void erow_append_string(struct erow *erow, const char *s, size_t s_len); -void erow_insert_char(struct erow *erow, int at, char c); -void erow_delete_char(struct erow *erow, int at); -int erow_cx_to_rx(struct erow *erow, int cx); -int erow_rx_to_cx(struct erow *erow, int rx); -void erow_free(struct erow *erow); #endif // BUFFER_H diff --git a/include/erow.h b/include/erow.h new file mode 100644 index 0000000..f197dea --- /dev/null +++ b/include/erow.h @@ -0,0 +1,23 @@ +#ifndef EROW_H +#define EROW_H + +#include + +struct erow { + char *chars; + size_t n_chars; + + char *rchars; + size_t n_rchars; + + struct buffer *buffer; +}; + +struct erow *erow_create(const char* chars, size_t n_chars, struct buffer *buffer); +void erow_insert_chars(struct erow *erow, const char *chars, size_t n_chars, int at); +void erow_delete_char(struct erow *erow, int at); +int erow_cx_to_rx(struct erow *erow, int cx); +int erow_rx_to_cx(struct erow *erow, int rx); +void erow_free(struct erow *erow); + +#endif // EROW_H diff --git a/include/kilo.h b/include/kilo.h index 3fc14f3..54ca860 100644 --- a/include/kilo.h +++ b/include/kilo.h @@ -7,8 +7,6 @@ #include struct editor_state { - int cx, cy, rx; - int row_off, col_off; int screenrows, screencols; int quit_times; diff --git a/src/buffer.c b/src/buffer.c index 12210b4..bf70f38 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,12 +1,8 @@ -#include -#include #include -#include #include -#include #include "buffer.h" -#include "kilo.h" +#include "erow.h" #include "utils.h" static void buffer_free_rows(struct buffer *buffer); @@ -16,75 +12,89 @@ struct buffer *buffer_create(void) { struct buffer *buffer = malloc(sizeof(struct buffer)); buffer->filename = NULL; + + buffer->cx = buffer->cy = buffer->rx = 0; + buffer->row_off = buffer->col_off = 0; + buffer->rows = NULL; buffer->n_rows = 0; + buffer->modified = false; return buffer; } -void buffer_read_file(struct buffer *buffer, const char *filename) { // TODO: Add error handling - if (buffer->filename) - free(buffer->filename); - - if (buffer->rows) - buffer_free_rows(buffer); +ERRCODE buffer_read_file(struct buffer *buffer, const char *filename) { + ERRCODE errcode = 0; + if (buffer->filename) free(buffer->filename); size_t filename_len = strlen(filename); buffer->filename = malloc(filename_len); memcpy(buffer->filename, filename, filename_len); + if (buffer->rows) + buffer_free_rows(buffer); + + size_t buf_cap = 64; + size_t buf_size = 0; + char *buf = malloc(buf_cap); + FILE *file = fopen(filename, "r"); - if (!file) die("fopen"); + if (file == NULL) + RETURN(-1); - size_t line_buffer_size = 64; - char *line_buffer = malloc(line_buffer_size); - - bool file_remaining = true; - while (file_remaining) { + while (true) { char c; - int i; - for (i = 0; (c = getc(file)) != '\n' && c != EOF; i++) { - if (i >= (int) line_buffer_size) - line_buffer = realloc(line_buffer, line_buffer_size *= 2); + while ((c = getc(file)) != '\n' && c != EOF) { + if (buf_size >= buf_cap) + buf = realloc(buf, buf_cap *= 2); - line_buffer[i] = c; + buf[buf_size++] = c; } - buffer_insert_row(buffer, line_buffer, i, buffer->n_rows); - file_remaining = (c != EOF); + struct erow* erow = erow_create(buf, buf_size, buffer); + buffer_insert_row(buffer, erow, buffer->n_rows); + + if (c == EOF) + break; } - free(line_buffer); - fclose(file); +END: + if (file != NULL) + fclose(file); + + if (buf != NULL) + free(buf); buffer->modified = false; + + return errcode; } -ERRCODE buffer_write_file(struct buffer *buffer, int *bytes_written) { +ERRCODE buffer_write_file(struct buffer *buffer, size_t *bytes_written) { ERRCODE errcode = 0; - int fd = -1; + FILE *file = NULL; char *write_buffer = NULL; if (buffer->filename == NULL) RETURN(-1); - size_t len; - write_buffer = buffer_get_string(buffer, &len); + size_t n_chars; + write_buffer = buffer_get_string(buffer, &n_chars); - fd = open(buffer->filename, O_RDWR | O_TRUNC | O_CREAT, 0644); - if (fd == -1) + file = fopen(buffer->filename, "w"); + if (file == NULL) RETURN(-2); - *bytes_written = write(fd, write_buffer, len); - if (*bytes_written != (ssize_t) len) + *bytes_written = fwrite(write_buffer, 1, n_chars, file); + if (*bytes_written != n_chars) RETURN(-3); END: - if (fd != -1) - close(fd); + if (file != NULL) + fclose(file); if (write_buffer) free(write_buffer); @@ -95,21 +105,41 @@ END: return errcode; } -void buffer_insert_row(struct buffer *buffer, const char *chars, int n_chars, int at) { // TODO: Make this take an erow - buffer->rows = realloc(buffer->rows, sizeof(struct erow) * (buffer->n_rows + 1)); - memmove(buffer->rows + at + 1, buffer->rows + at, sizeof(struct erow) * (buffer->n_rows - at)); - struct erow *erow = buffer->rows + at; +ERRCODE buffer_get_row_chars(struct buffer *buffer, char **chars, size_t *n_chars, int at) { + if (buffer == NULL) + return -1; - erow->chars = malloc(n_chars); - memcpy(erow->chars, chars, n_chars); - erow->n_chars = n_chars; + if (!(0 <= at && at <= buffer->n_rows)) + return -2; - erow->rchars = NULL; - erow->n_rchars = 0; + if (chars) *chars = buffer->rows[at]->chars; + if (n_chars) *n_chars = buffer->rows[at]->n_chars; - erow_update_rendering(erow); + return 0; +} + +ERRCODE buffer_get_row_rchars(struct buffer *buffer, char **rchars, size_t *n_rchars, int at) { + if (buffer == NULL) + return -1; + + if (!(0 <= at && at <= buffer->n_rows)) + return -2; + + if (rchars) *rchars = buffer->rows[at]->rchars; + if (n_rchars) *n_rchars = buffer->rows[at]->n_rchars; + + return 0; +} + +void buffer_insert_row(struct buffer *buffer, struct erow *erow, int at) { + if (!(0 <= at && at <= buffer->n_rows)) + return; + + buffer->rows = realloc(buffer->rows, sizeof(struct erow *) * (buffer->n_rows + 1)); + memmove(buffer->rows + at + 1, buffer->rows + at, sizeof(struct erow *) * (buffer->n_rows - at)); + + buffer->rows[at] = erow; - buffer->modified = true; buffer->n_rows++; } @@ -117,150 +147,47 @@ void buffer_delete_row(struct buffer *buffer, int at) { if (!(0 <= at && at < buffer->n_rows)) return; - erow_free(buffer->rows + at); - memmove(buffer->rows + at, buffer->rows + at + 1, sizeof(struct erow) * (buffer->n_rows - at - 1)); + erow_free(buffer->rows[at]); + memmove(buffer->rows + at, buffer->rows + at + 1, sizeof(struct erow *) * (buffer->n_rows - at - 1)); buffer->n_rows--; buffer->modified = true; } +struct erow *buffer_get_crow(struct buffer *buffer) { + return (buffer->cy < buffer->n_rows ? buffer->rows[buffer->cy]: NULL); +} + void buffer_free(struct buffer *buffer) { free(buffer->filename); + buffer_free_rows(buffer); } static void buffer_free_rows(struct buffer *buffer) { for (int i = 0; i < buffer->n_rows; i++) - erow_free(buffer->rows + i); + erow_free(buffer->rows[i]); + + free(buffer->rows); + + buffer->rows = NULL; + buffer->n_rows = 0; } -static char *buffer_get_string(struct buffer *buffer, size_t *len_p) { - *len_p = 0; +static char *buffer_get_string(struct buffer *buffer, size_t *n_chars) { + *n_chars = 0; for (int i = 0; i < buffer->n_rows; i++) - *len_p += buffer->rows[i].n_chars + 1; + *n_chars += buffer->rows[i]->n_chars + 1; - char *write_buffer = malloc(*len_p); - char *p = write_buffer; - for (int i = 0; i < buffer->n_rows; i++) { - struct erow *row = buffer->rows+i; - memcpy(p, row->chars, row->n_chars); - p += row->n_chars; - *p = '\n'; - p++; + char *write_buffer = malloc(*n_chars); + for (int i = 0, j = 0; i < buffer->n_rows; j++, i++) { + struct erow *row = buffer->rows[i]; + + memcpy(write_buffer + j, row->chars, row->n_chars); + j += row->n_chars; + write_buffer[j++] = '\n'; } return write_buffer; } - -/*****************************************************************************/ - -void erow_update_rendering(struct erow *erow) { - size_t line_buffer_size = 64; - char *line_buffer = malloc(line_buffer_size); - size_t n_rchars = 0; - - for (int i = 0; i < erow->n_chars; i++) { - switch (erow->chars[i]) { - case '\t': { - int spaces = KILO_TAB_STOP - (n_rchars % KILO_TAB_STOP); - while (spaces--) { - if (n_rchars >= line_buffer_size) - line_buffer = realloc(line_buffer, 2*line_buffer_size); - - line_buffer[n_rchars++] = ' '; - } - break; - } - default: - if (n_rchars >= line_buffer_size) - line_buffer = realloc(line_buffer, 2*line_buffer_size); - - line_buffer[n_rchars++] = erow->chars[i]; - } - } - - if (erow->rchars) - free(erow->rchars); - - erow->rchars = malloc(n_rchars); - erow->n_rchars = n_rchars; - memcpy(erow->rchars, line_buffer, n_rchars); - - free(line_buffer); -} - -void erow_append_string(struct erow *erow, const char *s, size_t s_len) { - erow->n_chars = erow->n_chars + s_len; - erow->chars = realloc(erow->chars, erow->n_chars); - - memcpy(erow->chars + erow->n_chars - s_len, s, s_len); - erow_update_rendering(erow); - - E.current_buf->modified = true; // TODO -} - -void erow_insert_char(struct erow *erow, int at, char c) { - if (!(0 <= at && at <= erow->n_chars)) - return; - - erow->chars = realloc(erow->chars, erow->n_chars + 1); - memmove(erow->chars + at + 1, erow->chars + at, erow->n_chars - at); - - erow->chars[at] = c; - erow->n_chars++; - - erow_update_rendering(erow); - - E.current_buf->modified = true; // TODO -} - -void erow_delete_char(struct erow *erow, int at) { - if (!(0 <= at && at < erow->n_chars)) - return; - - memmove(erow->chars + at, erow->chars + at + 1, erow->n_chars - at); - erow->n_chars--; - erow->chars = realloc(erow->chars, erow->n_chars); - E.cx--; - erow_update_rendering(erow); - - E.current_buf->modified = true; // TODO -} - -int erow_cx_to_rx(struct erow *erow, int cx) { - int rx = 0; - - if (erow == NULL) - return 0; - - for (int i = 0; i < MIN(cx, erow->n_chars); i++) { - if (erow->chars[i] == '\t') - rx += KILO_TAB_STOP - (rx % KILO_TAB_STOP); - else rx++; - } - - return rx; -} - -int erow_rx_to_cx(struct erow *erow, int rx) { - int cx = 0, _rx = 0; - - if (erow == NULL) - return 0; - - while (_rx < rx && cx < erow->n_chars) { - if (erow->chars[cx] == '\t') { - _rx += KILO_TAB_STOP - (_rx % KILO_TAB_STOP); - } else _rx++; - - cx++; - } - - return (_rx >= rx ? cx : erow->n_chars); -} - -void erow_free(struct erow *erow) { - if (erow->chars) free(erow->chars); - if (erow->rchars) free(erow->rchars); -} diff --git a/src/erow.c b/src/erow.c new file mode 100644 index 0000000..bcf0ea0 --- /dev/null +++ b/src/erow.c @@ -0,0 +1,111 @@ +#include +#include + +#include "buffer.h" +#include "erow.h" +#include "kilo.h" + +static void erow_update_rchars(struct erow *erow); + +struct erow *erow_create(const char* chars, size_t n_chars, struct buffer *buffer) { + struct erow *erow = malloc(sizeof(struct erow)); + + erow->chars = malloc(n_chars); + erow->n_chars = n_chars; + + memcpy(erow->chars, chars, erow->n_chars); + erow_update_rchars(erow); + + erow->buffer = buffer; + + return erow; +} + +void erow_insert_chars(struct erow *erow, const char *chars, size_t n_chars, int at) { + erow->chars = realloc(erow->chars, erow->n_chars + n_chars); + memmove(erow->chars + at + n_chars, erow->chars + at, n_chars - at); + memcpy(erow->chars + at, chars, n_chars); + + erow->n_chars += n_chars; + + erow_update_rchars(erow); + + if (erow->buffer) + erow->buffer->modified = true; +} + +void erow_delete_char(struct erow *erow, int at) { + memmove(erow->chars + at, erow->chars + at + 1, erow->n_chars - at); + erow->chars = realloc(erow->chars, --erow->n_chars); + + erow_update_rchars(erow); + + if (erow->buffer) + erow->buffer->modified = true; +} + +int erow_cx_to_rx(struct erow *erow, int cx) { + int rx = 0; + + for (char *c = erow->chars; c < erow->chars + cx; c++) { + if (*c == '\t') + rx += KILO_TAB_STOP - (rx % KILO_TAB_STOP); + else + rx++; + } + + return rx; +} + +int erow_rx_to_cx(struct erow *erow, int rx) { + if ((size_t) rx >= erow->n_rchars) + return erow->n_chars; + + int c_rx = 0; + + for (int cx = 0; (size_t) cx < erow->n_chars; cx++) { + if (erow->chars[cx] == '\t') + c_rx += KILO_TAB_STOP - (c_rx % KILO_TAB_STOP); + else + c_rx++; + + if (c_rx >= rx) + return cx; + } + + return erow->n_chars; +} + +void erow_free(struct erow *erow) { + if (erow->chars) free(erow->chars); + if (erow->rchars) free(erow->rchars); +} + +static void erow_update_rchars(struct erow *erow) { + if (erow->rchars) + free(erow->rchars); + + size_t n_rchars_max = 16; + erow->rchars = malloc(erow->n_rchars); + erow->n_rchars = 0; + + for (char *c = erow->chars; c < erow->chars + erow->n_chars; c++) { + if (*c == '\t') { + int spaces = KILO_TAB_STOP - (erow->n_rchars % KILO_TAB_STOP); + + while (erow->n_rchars + spaces > n_rchars_max) + erow->rchars = realloc(erow->rchars, n_rchars_max *= 2); + + memset(erow->rchars + erow->n_rchars, ' ', spaces); + erow->n_rchars += spaces; + } else { + if (erow->n_rchars + 1 > n_rchars_max) + erow->rchars = realloc(erow->rchars, n_rchars_max *= 2); + + erow->rchars[erow->n_rchars++] = *c; + } + } + + erow->rchars = realloc(erow->rchars, erow->n_rchars); +} + diff --git a/src/kilo.c b/src/kilo.c index 7d6a60f..8322192 100644 --- a/src/kilo.c +++ b/src/kilo.c @@ -36,9 +36,6 @@ void editor_init(char *filename) { if (terminal_enable_raw() == -1) die("term_enable_raw"); - E.cx = E.cy = E.rx = 0; - E.row_off = E.col_off = 0; - if (terminal_get_win_size(&E.screenrows, &E.screencols) == -1) die("term_get_win_size"); E.screenrows -= 2; @@ -61,39 +58,40 @@ void editor_set_message(const char *fmt, ...) { E.message_time = time(NULL); } -// TODO: Implement a fully featured line editor +// TODO: Implement a fully featured line editor here char *editor_prompt(const char *prompt) { - size_t buffer_size = 64; - char *buffer = malloc(buffer_size); + size_t buf_cap = 64; + size_t buf_size = 0; + char *buf = malloc(buf_cap); - size_t buffer_len = 0; - buffer[buffer_len] = '\0'; + buf[buf_size] = '\0'; while (true) { - editor_set_message(prompt, buffer); + editor_set_message(prompt, buf); ui_draw_screen(); KEY key = terminal_read_key(); - if (key == ESCAPE) { - editor_set_message(""); - free(buffer); - return NULL; - } else if (key == ENTER) { - if (buffer_len > 0) { - editor_set_message(""); - buffer = realloc(buffer, buffer_len + 1); - return buffer; - } + if (key == ESCAPE) goto failure; + else if (key == ENTER) { + if (buf_size > 0) goto success; + else goto failure; } else if (isprint(key)) { - if (buffer_len >= buffer_size - 1) { - buffer_size *= 2; - buffer = realloc(buffer, buffer_size); - } + if (buf_size + 1 >= buf_cap) + buf = realloc(buf, buf_cap *= 2); - buffer[buffer_len++] = key; - buffer[buffer_len] = '\0'; + buf[buf_size] = key; + buf_size++; + buf[buf_size] = '\0'; } } - return buffer; +failure: + free(buf); + buf = NULL; +success: + if (buf) + buf = realloc(buf, buf_size + 1); + + editor_set_message(""); + return buf; } diff --git a/src/ui.c b/src/ui.c index f000d6b..12e4089 100644 --- a/src/ui.c +++ b/src/ui.c @@ -28,8 +28,8 @@ void ui_draw_screen(void) { write(STDIN_FILENO, draw_buf->chars, draw_buf->n_chars); ab_free(draw_buf); - int row_pos = E.cy - E.row_off + 1; - int col_pos = E.rx - E.col_off + 1; + int row_pos = E.current_buf->cy - E.current_buf->row_off + 1; + int col_pos = E.current_buf->rx - E.current_buf->col_off + 1; if (terminal_set_cursor_pos(row_pos, col_pos) == -1) die("term_set_cursor_pos"); @@ -40,14 +40,17 @@ void ui_draw_screen(void) { static void ui_draw_rows(struct append_buf *draw_buf) { terminal_set_cursor_pos(1, 1); for (int y = 0; y < E.screenrows; y++) { - bool in_file = (y < E.current_buf->n_rows - E.row_off); - bool no_file = (E.current_buf->n_rows == 0); + bool in_file = (y < E.current_buf->n_rows - E.current_buf->row_off); + bool no_file = (E.current_buf->filename == NULL && E.current_buf->n_rows == 0); if (in_file) { - struct erow erow = E.current_buf->rows[y + E.row_off]; - int max_len = MIN(erow.n_rchars - E.col_off, E.screencols); + struct erow *crow = buffer_get_crow(E.current_buf); - ab_append(draw_buf, erow.rchars + E.col_off, MAX(max_len, 0)); + size_t len = crow->n_rchars - E.current_buf->col_off; + len = MIN(len, (size_t) E.screencols); + len = MAX(len, 0); + + ab_append(draw_buf, crow->rchars + E.current_buf->col_off, len); } else if (no_file && y == E.screenrows / 2) { char welcome[64]; int len = snprintf(welcome, sizeof(welcome), @@ -78,7 +81,7 @@ static void ui_draw_statusbar(struct append_buf *draw_buf) { len = sprintf(buf, "%s %s-- %d lines", display, modified, n_rows); memcpy(status_buf, buf, len); - len = sprintf(buf, "%d:%d", E.cy + 1, E.rx + 1); + len = sprintf(buf, "%d:%d", E.current_buf->cy + 1, E.current_buf->rx + 1); memcpy(status_buf + E.screencols - len, buf, len); ab_append(draw_buf, status_buf, E.screencols);