From 2f560997e5ad75160ad84fae81350f5d3451d3ec Mon Sep 17 00:00:00 2001 From: Hadeed Ahmad Date: Sat, 9 Sep 2023 01:32:29 +0500 Subject: [PATCH] Finish refactoring --- README.md | 2 +- include/buffer.h | 5 +- include/cursor.h | 10 +++ include/erow.h | 4 +- include/input.h | 4 +- include/utils.h | 3 +- progress_log.md | 9 +++ src/buffer.c | 11 ++- src/commands.c | 192 ++++++++++++----------------------------------- src/cursor.c | 45 +++++++++++ src/erow.c | 30 +++++--- src/input.c | 10 +-- src/kilo.c | 2 +- src/ui.c | 3 +- 14 files changed, 160 insertions(+), 170 deletions(-) create mode 100644 include/cursor.h create mode 100644 src/cursor.c diff --git a/README.md b/README.md index 830fcca..64786d5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ gcc -I include src/*.c -o kilo ## My additions - Split it up into multiple files and tried to follow good design and -organization practices (WIP). +organization practices. ## References - [antirez/kilo][1] diff --git a/include/buffer.h b/include/buffer.h index c588c02..28cc892 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -4,8 +4,10 @@ #include #include -#include "utils.h" #include "erow.h" +#include "utils.h" + +struct erow; struct buffer { char *filename; @@ -25,6 +27,7 @@ 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); struct erow *buffer_get_crow(struct buffer *buffer); +size_t buffer_get_crow_len(struct buffer *buffer); void buffer_free(struct buffer *buffer); diff --git a/include/cursor.h b/include/cursor.h new file mode 100644 index 0000000..636aef6 --- /dev/null +++ b/include/cursor.h @@ -0,0 +1,10 @@ +#ifndef CURSOR_H +#define CURSOR_H + +#include "buffer.h" + +struct buffer; + +void cursor_move(struct buffer *buffer, int dx, int dy); + +#endif // CURSOR_H diff --git a/include/erow.h b/include/erow.h index f197dea..32bf887 100644 --- a/include/erow.h +++ b/include/erow.h @@ -3,6 +3,8 @@ #include +struct buffer; + struct erow { char *chars; size_t n_chars; @@ -15,7 +17,7 @@ struct erow { 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); +void erow_delete_chars(struct erow *erow, size_t n_chars, 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); diff --git a/include/input.h b/include/input.h index 2c0381a..64cf767 100644 --- a/include/input.h +++ b/include/input.h @@ -3,6 +3,8 @@ #include +#define CTRL_KEY(key) ((key) & 0x1f) + enum KEYS { TAB = 9, ENTER = 13, @@ -21,6 +23,6 @@ enum KEYS { }; typedef uint16_t KEY; -void input_process_key(void); +void input_process_key(KEY c); #endif // INPUT_H diff --git a/include/utils.h b/include/utils.h index 063bfac..0cda7a5 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,10 +1,9 @@ #ifndef UTIL_H #define UTIL_H -#define CTRL_KEY(key) ((key) & 0x1f) - #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) +#define CLAMP(value, min, max) MIN(MAX(value, min), max) #define _STRINGIZE(x) #x #define STRINGIZE(x) _STRINGIZE(x) diff --git a/progress_log.md b/progress_log.md index d0fc474..a9fc5cf 100644 --- a/progress_log.md +++ b/progress_log.md @@ -1,4 +1,5 @@ # Progress log + - (4/9/2023) Finished 5 chapters out of 7, basic text editing is fully possible. It has fun been working on this, but semester is starting now and I might not get to work on this a lot anymore. It might have lots of bugs, and @@ -6,3 +7,11 @@ there's lots of things I don't like, there are TODOs everywhere. So many leaky abstractions, near the end I started blindly following the tutorial and didn't stop to think how it fits with the abstractions I have defined, but I might fix it all one of these days... + +- (9/9/2023) Didn't implement any new features, but refactored heavily. All the +previous features should be working unless I introduced a bug. But I like the +abstractions better now, and I cleaned up most of TODOs. This was painful to +get right and I contemplated quitting, but proud of myself. I find it very +interesting that the total of amount of code barely changed, it actually +decreased as measured by tokei. It increased slightly in the header files and +decreased in the source files, yet it was a lot of work. diff --git a/src/buffer.c b/src/buffer.c index bf70f38..a016323 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,4 +1,6 @@ +#include #include +#include #include #include "buffer.h" @@ -36,7 +38,6 @@ ERRCODE buffer_read_file(struct buffer *buffer, const char *filename) { buffer_free_rows(buffer); size_t buf_cap = 64; - size_t buf_size = 0; char *buf = malloc(buf_cap); FILE *file = fopen(filename, "r"); @@ -45,6 +46,7 @@ ERRCODE buffer_read_file(struct buffer *buffer, const char *filename) { while (true) { char c; + size_t buf_size = 0; while ((c = getc(file)) != '\n' && c != EOF) { if (buf_size >= buf_cap) @@ -139,7 +141,6 @@ void buffer_insert_row(struct buffer *buffer, struct erow *erow, int at) { memmove(buffer->rows + at + 1, buffer->rows + at, sizeof(struct erow *) * (buffer->n_rows - at)); buffer->rows[at] = erow; - buffer->n_rows++; } @@ -164,6 +165,12 @@ void buffer_free(struct buffer *buffer) { buffer_free_rows(buffer); } +size_t buffer_get_crow_len(struct buffer *buffer) { + struct erow *crow = buffer_get_crow(buffer); + + return crow ? crow->n_chars : 0; +} + static void buffer_free_rows(struct buffer *buffer) { for (int i = 0; i < buffer->n_rows; i++) erow_free(buffer->rows[i]); diff --git a/src/commands.c b/src/commands.c index 5655dec..570f7a6 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,19 +1,16 @@ #include -#include #include #include #include "buffer.h" #include "commands.h" +#include "cursor.h" +#include "erow.h" #include "input.h" #include "kilo.h" #include "terminal.h" #include "utils.h" -static void cursor_adjust_viewport(void); -static void cursor_check_file_bounds(bool horizontal); -static void cursor_update_rx(bool horizontal); - void command_quit(void) { if (E.current_buf->modified && E.quit_times) { editor_set_message("Unsaved changed! Press quit %d more time(s)", E.quit_times); @@ -25,110 +22,87 @@ void command_quit(void) { } void command_move_cursor(KEY key) { - struct erow *rows = E.current_buf->rows; - int n_rows = E.current_buf->n_rows; - - struct erow *erow = (rows && E.cy < n_rows ? rows + E.cy : NULL); - int max_x = (erow ? erow->n_chars : 0); - switch (key) { case ARROW_LEFT: - E.cx--; + cursor_move(E.current_buf, -1, 0); break; case ARROW_DOWN: - E.cy++; + cursor_move(E.current_buf, 0, 1); break; case ARROW_UP: - E.cy--; + cursor_move(E.current_buf, 0, -1); break; case ARROW_RIGHT: - E.cx++; + cursor_move(E.current_buf, 1, 0); break; case HOME: - E.cx = 0; + cursor_move(E.current_buf, -E.current_buf->cx, 0); break; case END: - E.cx = max_x; + cursor_move(E.current_buf, buffer_get_crow_len(E.current_buf) - E.current_buf->cx, 0); break; case PG_UP: - E.cy -= E.screenrows; + cursor_move(E.current_buf, 0, -E.screenrows); break; case PG_DOWN: - E.cy += E.screenrows; + cursor_move(E.current_buf, 0, E.screenrows); break; } - - bool horizontal = (key == ARROW_LEFT || - key == ARROW_RIGHT || - key == HOME || - key == END); - - cursor_check_file_bounds(horizontal); - cursor_update_rx(horizontal); - cursor_adjust_viewport(); } -// TODO: Improve this void command_insert_line(void) { - if (E.cy == E.current_buf->n_rows) { - buffer_insert_row(E.current_buf, NULL, 0, E.cy); + struct erow *erow = erow_create(NULL, 0, E.current_buf); + + if (E.current_buf->cy == E.current_buf->n_rows) { + buffer_insert_row(E.current_buf, erow, E.current_buf->cy); } else { - buffer_insert_row(E.current_buf, NULL, 0, E.cy + 1); + buffer_insert_row(E.current_buf, erow, E.current_buf->cy + 1); - struct erow *c_row = E.current_buf->rows + E.cy; - struct erow *n_row = E.current_buf->rows + E.cy + 1; + struct erow *crow = buffer_get_crow(E.current_buf); - erow_append_string(n_row, c_row->chars + E.cx, c_row->n_chars - E.cx); - - c_row->chars = realloc(c_row->chars, E.cx); - c_row->n_chars = E.cx; - erow_update_rendering(c_row); + erow_insert_chars(erow, crow->chars + E.current_buf->cx, crow->n_chars - E.current_buf->cx, 0); + erow_delete_chars(crow, crow->n_chars - E.current_buf->cx, E.current_buf->cx); } - E.cx = E.rx = 0; - E.cy++; - - cursor_adjust_viewport(); + input_process_key(ARROW_DOWN); + input_process_key(HOME); } void command_insert_char(char c) { - if (E.cy == E.current_buf->n_rows) - buffer_insert_row(E.current_buf, NULL, 0, E.current_buf->n_rows); + struct erow *erow; - erow_insert_char(E.current_buf->rows+E.cy, E.cx++, c); - E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx); + if (E.current_buf->cy == E.current_buf->n_rows) { + erow = erow_create(NULL, 0, E.current_buf); + buffer_insert_row(E.current_buf, erow, E.current_buf->n_rows); + } else erow = buffer_get_crow(E.current_buf); - cursor_adjust_viewport(); + erow_insert_chars(erow, &c, 1, E.current_buf->cx); + input_process_key(ARROW_RIGHT); } -void command_delete_char(void) { // TODO - if (E.cx == 0 && E.cy == 0) return; - if (E.cy == E.current_buf->n_rows) { - E.cx = E.current_buf->rows[--E.cy].n_chars; - E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx); - return; +void command_delete_char(void) { + if (E.current_buf->cy == E.current_buf->n_rows) + input_process_key(ARROW_LEFT); + + struct erow *crow = buffer_get_crow(E.current_buf); + + if (E.current_buf->cx == 0) { + if (E.current_buf->cy == 0) + return; + + struct erow *prow = E.current_buf->rows[E.current_buf->cy - 1]; + + input_process_key(ARROW_UP); + cursor_move(E.current_buf, prow->n_chars, 0); + + erow_insert_chars(prow, crow->chars, crow->n_chars, prow->n_chars); + buffer_delete_row(E.current_buf, E.current_buf->cy + 1); + } else { + erow_delete_chars(crow, 1, E.current_buf->cx - 1); + input_process_key(ARROW_LEFT); } - - if (E.cx > 0) { - erow_delete_char(E.current_buf->rows+E.cy, E.cx - 1); - } - else { - struct erow *c_row = E.current_buf->rows + E.cy; - struct erow *p_row = E.current_buf->rows + E.cy - 1; - - E.cx = p_row->n_chars; - - erow_append_string(p_row, c_row->chars, c_row->n_chars); - buffer_delete_row(E.current_buf, E.cy); - - E.cy--; - } - - E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx); - - cursor_adjust_viewport(); } void command_save_buffer(void) { @@ -140,79 +114,11 @@ void command_save_buffer(void) { } } - int bytes_written; + size_t bytes_written; ERRCODE errcode = buffer_write_file(E.current_buf, &bytes_written); if (errcode == 0) editor_set_message("%d bytes written", bytes_written); else - editor_set_message("Save failed: ERRCODE %d: %s", errcode, strerror(errno)); -} - -static void cursor_check_file_bounds(bool horizontal) { - struct erow *rows = E.current_buf->rows; - int n_rows = E.current_buf->n_rows; - - struct erow *erow = (rows && E.cy < n_rows ? rows + E.cy : NULL); - int max_x = (erow ? erow->n_chars : 0); - - if (E.cy < 0) - E.cy = 0; - - if (E.cy > n_rows) - E.cy = n_rows; - - if (E.cx < 0) { - if (E.cy > 0 && horizontal) - { - erow = &rows[--E.cy]; - max_x = erow->n_chars; - - E.cx = max_x; - } - else E.cx = 0; - } - - if (E.cx > max_x) { - if (E.cy < n_rows && horizontal) { - E.cx = 0; - E.cy++; - } else E.cx = max_x; - } -} - -static void cursor_update_rx(bool horizontal) { - struct erow *rows = E.current_buf->rows; - int n_rows = E.current_buf->n_rows; - - struct erow *erow = (rows && E.cy < n_rows ? rows + E.cy : NULL); - int max_x = (erow ? erow->n_chars : 0); - - static int saved_rx = 0; - if (horizontal) saved_rx = erow_cx_to_rx(erow, E.cx); - else { - E.cx = erow_rx_to_cx(erow, saved_rx); - if (E.cx > max_x) - E.cx = max_x; - } - - E.rx = erow_cx_to_rx(rows + E.cy, E.cx); -} - -static void cursor_adjust_viewport(void) { - int max_row_off = E.cy; - if (E.row_off > max_row_off) - E.row_off = max_row_off; - - int max_col_off = E.rx; - if (E.col_off > max_col_off) - E.col_off = max_col_off; - - int min_row_off = E.cy - (E.screenrows - 1); - if (E.row_off < min_row_off) - E.row_off = min_row_off; - - int min_col_off = E.rx - (E.screencols - 1); - if (E.col_off < min_col_off) - E.col_off = min_col_off; + editor_set_message("Write error %d: %s", errcode, strerror(errno)); } diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 0000000..98f5550 --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,45 @@ +#include "buffer.h" +#include "cursor.h" +#include "erow.h" +#include "kilo.h" +#include "utils.h" + +static void cursor_adjust_viewport(struct buffer *buffer); + +void cursor_move(struct buffer *buffer, int dx, int dy) { + static int saved_rx = 0; + + buffer->cy = CLAMP(buffer->cy + dy, 0, buffer->n_rows); + + if (dx == 0) + E.current_buf->cx = erow_rx_to_cx(buffer_get_crow(E.current_buf), saved_rx); + + int new_x = buffer->cx + dx, max_x = buffer_get_crow_len(buffer); + if (new_x < 0) { + if (buffer->cy > 0) { + buffer->cy--; + buffer->cx = buffer_get_crow_len(buffer); + } else buffer->cx = 0; + } else if (new_x > max_x) { + if (buffer->cy < buffer->n_rows) { + buffer->cy++; + buffer->cx = 0; + } else buffer->cx = max_x; + } else buffer->cx = new_x; + + cursor_adjust_viewport(buffer); + buffer->rx = erow_cx_to_rx(buffer_get_crow(buffer), buffer->cx); + + if (dy == 0) + saved_rx = buffer->rx; +} + +static void cursor_adjust_viewport(struct buffer *buffer) { + int min_row_off = buffer->cy - (E.screenrows - 1); + int max_row_off = buffer->cy; + buffer->row_off = CLAMP(buffer->row_off, min_row_off, max_row_off); + + int min_col_off = buffer->rx - (E.screencols - 1); + int max_col_off = buffer->rx; + buffer->col_off = CLAMP(buffer->col_off, min_col_off, max_col_off); +} diff --git a/src/erow.c b/src/erow.c index bcf0ea0..9da9f7f 100644 --- a/src/erow.c +++ b/src/erow.c @@ -1,9 +1,11 @@ +#include #include #include #include "buffer.h" #include "erow.h" #include "kilo.h" +#include "utils.h" static void erow_update_rchars(struct erow *erow); @@ -14,6 +16,8 @@ struct erow *erow_create(const char* chars, size_t n_chars, struct buffer *buffe erow->n_chars = n_chars; memcpy(erow->chars, chars, erow->n_chars); + + erow->rchars = NULL; erow_update_rchars(erow); erow->buffer = buffer; @@ -23,7 +27,7 @@ struct erow *erow_create(const char* chars, size_t n_chars, struct buffer *buffe 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); + memmove(erow->chars + at + n_chars, erow->chars + at, erow->n_chars - at); memcpy(erow->chars + at, chars, n_chars); erow->n_chars += n_chars; @@ -34,9 +38,9 @@ void erow_insert_chars(struct erow *erow, const char *chars, size_t n_chars, int 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); +void erow_delete_chars(struct erow *erow, size_t n_chars, int at) { + memmove(erow->chars + at, erow->chars + at + n_chars, erow->n_chars - at - n_chars); + erow->chars = realloc(erow->chars, erow->n_chars -= n_chars); erow_update_rchars(erow); @@ -45,6 +49,11 @@ void erow_delete_char(struct erow *erow, int at) { } int erow_cx_to_rx(struct erow *erow, int cx) { + if (erow == NULL) + return 0; + + cx = CLAMP(cx, 0, (int) erow->n_chars); + int rx = 0; for (char *c = erow->chars; c < erow->chars + cx; c++) { @@ -58,17 +67,18 @@ int erow_cx_to_rx(struct erow *erow, int cx) { } int erow_rx_to_cx(struct erow *erow, int rx) { - if ((size_t) rx >= erow->n_rchars) - return erow->n_chars; + if (erow == NULL) + return 0; - int c_rx = 0; - - for (int cx = 0; (size_t) cx < erow->n_chars; cx++) { + int cx = 0, c_rx = 0; + while ((size_t) cx < erow->n_chars) { if (erow->chars[cx] == '\t') c_rx += KILO_TAB_STOP - (c_rx % KILO_TAB_STOP); else c_rx++; + cx++; + if (c_rx >= rx) return cx; } @@ -86,7 +96,7 @@ static void erow_update_rchars(struct erow *erow) { free(erow->rchars); size_t n_rchars_max = 16; - erow->rchars = malloc(erow->n_rchars); + erow->rchars = malloc(n_rchars_max); erow->n_rchars = 0; for (char *c = erow->chars; c < erow->chars + erow->n_chars; c++) { diff --git a/src/input.c b/src/input.c index bc5c75c..6bae6d2 100644 --- a/src/input.c +++ b/src/input.c @@ -1,12 +1,8 @@ #include "commands.h" #include "input.h" #include "kilo.h" -#include "terminal.h" -#include "utils.h" - -void input_process_key(void) { - KEY c = terminal_read_key(); +void input_process_key(KEY c) { switch (c) { case ARROW_LEFT: case ARROW_DOWN: @@ -20,7 +16,7 @@ void input_process_key(void) { break; case CTRL_KEY('S'): - command_save_buffer(); + // command_save_buffer(); break; case ENTER: @@ -28,7 +24,7 @@ void input_process_key(void) { break; case DEL: - command_move_cursor(ARROW_RIGHT); + input_process_key(ARROW_RIGHT); case BACKSPACE: case CTRL_KEY('H'): command_delete_char(); diff --git a/src/kilo.c b/src/kilo.c index 8322192..06113f1 100644 --- a/src/kilo.c +++ b/src/kilo.c @@ -22,7 +22,7 @@ int main(int argc, char **argv) { while (true) { ui_draw_screen(); - input_process_key(); + input_process_key(terminal_read_key()); } return 0; diff --git a/src/ui.c b/src/ui.c index 12e4089..f21cce9 100644 --- a/src/ui.c +++ b/src/ui.c @@ -6,6 +6,7 @@ #include #include "buffer.h" +#include "erow.h" #include "kilo.h" #include "terminal.h" #include "ui.h" @@ -44,7 +45,7 @@ static void ui_draw_rows(struct append_buf *draw_buf) { bool no_file = (E.current_buf->filename == NULL && E.current_buf->n_rows == 0); if (in_file) { - struct erow *crow = buffer_get_crow(E.current_buf); + struct erow *crow = E.current_buf->rows[y + E.current_buf->row_off]; size_t len = crow->n_rchars - E.current_buf->col_off; len = MIN(len, (size_t) E.screencols);