Commit 0939238
Changed files (1)
kilo.c
@@ -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();