diff options
| author | mo khan <mo@mokhan.ca> | 2021-07-25 21:31:48 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2021-07-25 21:31:48 -0600 |
| commit | 093923871d94137f9c3660c9fd1dd2f7521dc89a (patch) | |
| tree | 5fb41da66e5393ee98b2ea2223a4c018d49172b4 | |
| parent | 5da2b926bcbd06a4cfbd170f7bf58037951d9dbd (diff) | |
| -rw-r--r-- | kilo.c | 233 |
1 files changed, 216 insertions, 17 deletions
@@ -1,17 +1,26 @@ /*** includes ***/ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE +#define _GNU_SOURCE + #include <ctype.h> #include <errno.h> #include <stdio.h> +#include <stdarg.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> +#include <sys/types.h> #include <termios.h> +#include <time.h> #include <unistd.h> /*** defines ***/ #define KILO_VERSION "0.0.1" +#define KILO_TAB_STOP 8 + #define CTRL_KEY(k) ((k) & 0x1f) enum editorKey { @@ -28,10 +37,25 @@ enum editorKey { /*** data ***/ +typedef struct erow { + int size; + int rsize; + char *chars; + char *render; +} erow; + struct editor_config { int cx, cy; + int rx; + int rowoff; + int coloff; int screenrows; int screencols; + int numrows; + erow *row; + char *filename; + char statusmsg[80]; + time_t statusmsg_time; struct termios orig_termios; }; @@ -149,6 +173,78 @@ int get_window_size(int *rows, int *cols) { } } +/*** row operations ***/ + +int editor_row_cx_to_rx(erow *row, int cx) { + int rx = 0; + int j; + for (j = 0; j < cx; j++) { + if (row->chars[j] == '\t') + rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP); + rx++; + } + return rx; +} + +void editor_update_row(erow *row) { + int tabs = 0; + int j; + for (j = 0; j < row->size; j++) + if (row->chars[j] == '\t') tabs++; + + free(row->render); + row->render = malloc(row->size + tabs*(KILO_TAB_STOP - 1) + 1); + + int idx = 0; + for (j = 0; j < row->size; j++) { + if (row->chars[j] == '\t') { + row->render[idx++] = ' '; + while (idx % KILO_TAB_STOP != 0) row->render[idx++] = ' '; + } else { + row->render[idx++] = row->chars[j]; + } + } + row->render[idx] = '\0'; + row->rsize = idx; +} + +void editor_append_row(char *s, size_t len) { + E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1)); + + int at = E.numrows; + E.row[at].size = len; + E.row[at].chars = malloc(len + 1); + memcpy(E.row[at].chars, s, len); + E.row[at].chars[len] = '\0'; + + E.row[at].rsize = 0; + E.row[at].render = NULL; + editor_update_row(&E.row[at]); + + E.numrows++; +} + +/*** file i/o ***/ + +void editor_open(char *filename) { + free(E.filename); + E.filename = strdup(filename); + + FILE *fp = fopen(filename, "r"); + if (!fp) die("fopen"); + + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + while ((linelen = getline(&line, &linecap, fp)) != -1) { + while (linelen > 0 && (line[linelen - 1] == '\n' || line[linelen - 1] == '\r')) + linelen--; + editor_append_row(line, linelen); + } + free(line); + fclose(fp); +} + /*** append buffer ***/ struct abuf { @@ -173,40 +269,99 @@ void ab_free(struct abuf *ab) { /*** output ***/ +void editor_scroll() { + E.rx = 0; + if (E.cy < E.numrows) { + E.rx = editor_row_cx_to_rx(&E.row[E.cy], E.cx); + } + + if (E.cy < E.rowoff) { + E.rowoff = E.cy; + } + if (E.cy >= E.rowoff + E.screenrows) { + E.rowoff = E.cy - E.screenrows + 1; + } + if (E.rx < E.coloff) { + E.coloff = E.rx; + } + if (E.rx >= E.coloff + E.screencols) { + E.coloff = E.rx - E.screencols + 1; + } +} + void editor_draw_rows(struct abuf *ab) { for (int i = 0; i < E.screenrows; i++) { - if (i == E.screenrows / 3) { - char welcome[80]; - int length = snprintf(welcome, sizeof(welcome), "Kilo editor -- version %s", KILO_VERSION); - if (length > E.screencols) length = E.screencols; - int padding = (E.screencols - length) / 2; - if (padding) { + int filerow = i + E.rowoff; + if (filerow >= E.numrows) { + if (E.numrows == 0 && i == E.screenrows / 3) { + char welcome[80]; + int length = snprintf(welcome, sizeof(welcome), + "Kilo editor -- version %s", KILO_VERSION); + if (length > E.screencols) length = E.screencols; + int padding = (E.screencols - length) / 2; + if (padding) { + ab_append(ab, "~", 1); + padding--; + } + while (padding--) ab_append(ab, " ", 1); + ab_append(ab, welcome, length); + } else { ab_append(ab, "~", 1); - padding--; } - while (padding--) ab_append(ab, " ", 1); - ab_append(ab, welcome, length); } else { - ab_append(ab, "~", 1); + int len = E.row[filerow].rsize - E.coloff; + if (len < 0) len = 0; + if (len > E.screencols) len = E.screencols; + ab_append(ab, &E.row[filerow].render[E.coloff], len); } ab_append(ab, "\x1b[K", 3); - if (i < E.screenrows - 1) { - ab_append(ab, "\r\n", 2); + ab_append(ab, "\r\n", 2); + } +} + +void editor_draw_status_bar(struct abuf *ab) { + ab_append(ab, "\x1b[7m", 4); + char status[80], rstatus[80]; + int len = snprintf(status, sizeof(status), "%.20s - %d lines", E.filename ? E.filename : "[No Name]", E.numrows); + int rlen = snprintf(rstatus, sizeof(rstatus), "%d/%d", E.cy + 1, E.numrows); + if (len > E.screencols) len = E.screencols; + ab_append(ab, status, len); + while (len < E.screencols) { + if (E.screencols - len == rlen) { + ab_append(ab, rstatus, rlen); + break; + } else { + ab_append(ab, " ", 1); + len++; } } + ab_append(ab, "\x1b[m", 3); + ab_append(ab, "\r\n", 2); +} + +void editor_draw_message_bar(struct abuf *ab) { + ab_append(ab, "\x1b[K", 3); + int msglen = strlen(E.statusmsg); + if (msglen > E.screencols) msglen = E.screencols; + if (msglen && time(NULL) - E.statusmsg_time < 5) + ab_append(ab, E.statusmsg, msglen); } void editor_refresh_screen() { + editor_scroll(); + struct abuf ab = ABUF_INIT; ab_append(&ab, "\x1b[?25l", 6); ab_append(&ab, "\x1b[H", 3); // https://vt100.net/docs/vt100-ug/chapter3.html#CUP editor_draw_rows(&ab); + editor_draw_status_bar(&ab); + editor_draw_message_bar(&ab); char buf[32]; - snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy + 1, E.cx + 1); + snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.rx - E.coloff) + 1); ab_append(&ab, buf, strlen(buf)); ab_append(&ab, "\x1b[?25h", 6); @@ -215,18 +370,34 @@ void editor_refresh_screen() { ab_free(&ab); } +void editor_set_status_message(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap); + va_end(ap); + E.statusmsg_time = time(NULL); +} + /*** input ***/ void editor_move_cursor(int key) { + erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; + switch(key) { case ARROW_LEFT: if (E.cx != 0) { E.cx--; + } else if (E.cy > 0) { + E.cy--; + E.cx = E.row[E.cy].size; } break; case ARROW_RIGHT: - if (E.cx != E.screencols - 1) { + if (row && E.cx < row->size) { E.cx++; + } else if (row && E.cx == row->size) { + E.cy++; + E.cx = 0; } break; case ARROW_UP: @@ -235,11 +406,17 @@ void editor_move_cursor(int key) { } break; case ARROW_DOWN: - if (E.cy != E.screenrows - 1) { + if (E.cy < E.numrows) { E.cy++; } break; } + + row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; + int rowlen = row ? row->size : 0; + if (E.cx > rowlen) { + E.cx = rowlen; + } } void editor_process_keypress() { @@ -257,12 +434,20 @@ void editor_process_keypress() { break; case END_KEY: - E.cx = E.screencols - 1; + if (E.cy < E.numrows) + E.cx = E.row[E.cy].size; break; case PAGE_UP: case PAGE_DOWN: { + if (c == PAGE_UP) { + E.cy = E.rowoff; + } else if (c == PAGE_DOWN) { + E.cy = E.rowoff + E.screenrows - 1; + if (E.cy > E.numrows) E.cy = E.numrows; + } + int times = E.screenrows; while (times--) editor_move_cursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); @@ -290,14 +475,28 @@ void editor_process_keypress() { void init_editor() { E.cx = 0; E.cy = 0; + E.rx = 0; + E.rowoff = 0; + E.coloff = 0; + E.numrows = 0; + E.row = NULL; + E.filename = NULL; + E.statusmsg[0] = '\0'; + E.statusmsg_time = 0; if (get_window_size(&E.screenrows, &E.screencols) == -1) die("get_window_size"); + E.screenrows -= 2; } -int main() { +int main(int argc, char *argv[]) { enable_raw_mode(); init_editor(); + if (argc >= 2) { + editor_open(argv[1]); + } + + editor_set_status_message("HELP: Ctrl-Q = quit"); while (1) { editor_refresh_screen(); |
