Commit 5da2b92
kilo.c
@@ -11,11 +11,25 @@
/*** defines ***/
+#define KILO_VERSION "0.0.1"
#define CTRL_KEY(k) ((k) & 0x1f)
+enum editorKey {
+ ARROW_LEFT = 1000,
+ ARROW_RIGHT,
+ ARROW_UP,
+ ARROW_DOWN,
+ DEL_KEY,
+ HOME_KEY,
+ END_KEY,
+ PAGE_UP,
+ PAGE_DOWN
+};
+
/*** data ***/
struct editor_config {
+ int cx, cy;
int screenrows;
int screencols;
struct termios orig_termios;
@@ -53,13 +67,54 @@ void enable_raw_mode() {
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr");
}
-char editor_read_key() {
+int editor_read_key() {
int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if (nread == -1 && errno != EAGAIN) die("read");
}
- return c;
+
+ if (c == '\x1b') {
+ char seq[3];
+
+ if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b';
+ if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b';
+
+ if (seq[0] == '[') {
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b';
+ if (seq[2] == '~') {
+ switch (seq[1]) {
+ case '1': return HOME_KEY;
+ case '3': return DEL_KEY;
+ case '4': return END_KEY;
+ case '5': return PAGE_UP;
+ case '6': return PAGE_DOWN;
+ case '7': return HOME_KEY;
+ case '8': return END_KEY;
+ }
+ }
+ } else {
+ switch (seq[1]) {
+ case 'A': return ARROW_UP;
+ case 'B': return ARROW_DOWN;
+ case 'C': return ARROW_RIGHT;
+ case 'D': return ARROW_LEFT;
+ case 'H': return HOME_KEY;
+ case 'F': return END_KEY;
+ }
+ }
+ } else if (seq[0] == 'O') {
+ switch(seq[1]) {
+ case 'H': return HOME_KEY;
+ case 'F': return END_KEY;
+ }
+ }
+
+ return '\x1b';
+ } else {
+ return c;
+ }
}
int get_cursor_position(int *rows, int *cols) {
@@ -120,8 +175,22 @@ void ab_free(struct abuf *ab) {
void editor_draw_rows(struct abuf *ab) {
for (int i = 0; i < E.screenrows; i++) {
- ab_append(ab, "~", 1);
+ 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) {
+ ab_append(ab, "~", 1);
+ padding--;
+ }
+ while (padding--) ab_append(ab, " ", 1);
+ ab_append(ab, welcome, length);
+ } else {
+ ab_append(ab, "~", 1);
+ }
+ ab_append(ab, "\x1b[K", 3);
if (i < E.screenrows - 1) {
ab_append(ab, "\r\n", 2);
}
@@ -131,16 +200,16 @@ void editor_draw_rows(struct abuf *ab) {
void editor_refresh_screen() {
struct abuf ab = ABUF_INIT;
- // <esc>[0J clear screen from cursor up
- // <esc>[1J clear screen up to cursor
- // <esc>[2J clear entire screen
- ab_append(&ab, "\x1b[2J", 4);
- // reposition cursor at the top-left corner
+ 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);
- ab_append(&ab, "\x1b[H", 3);
+ char buf[32];
+ snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy + 1, E.cx + 1);
+ ab_append(&ab, buf, strlen(buf));
+
+ ab_append(&ab, "\x1b[?25h", 6);
write(STDOUT_FILENO, ab.b, ab.len);
ab_free(&ab);
@@ -148,8 +217,33 @@ void editor_refresh_screen() {
/*** input ***/
+void editor_move_cursor(int key) {
+ switch(key) {
+ case ARROW_LEFT:
+ if (E.cx != 0) {
+ E.cx--;
+ }
+ break;
+ case ARROW_RIGHT:
+ if (E.cx != E.screencols - 1) {
+ E.cx++;
+ }
+ break;
+ case ARROW_UP:
+ if (E.cy != 0) {
+ E.cy--;
+ }
+ break;
+ case ARROW_DOWN:
+ if (E.cy != E.screenrows - 1) {
+ E.cy++;
+ }
+ break;
+ }
+}
+
void editor_process_keypress() {
- char c = editor_read_key();
+ int c = editor_read_key();
switch(c) {
case CTRL_KEY('q'):
@@ -157,6 +251,31 @@ void editor_process_keypress() {
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
+
+ case HOME_KEY:
+ E.cx = 0;
+ break;
+
+ case END_KEY:
+ E.cx = E.screencols - 1;
+ break;
+
+ case PAGE_UP:
+ case PAGE_DOWN:
+ {
+ int times = E.screenrows;
+ while (times--)
+ editor_move_cursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
+ }
+ break;
+
+ case ARROW_UP:
+ case ARROW_DOWN:
+ case ARROW_LEFT:
+ case ARROW_RIGHT:
+ editor_move_cursor(c);
+ break;
+
default:
if (iscntrl(c))
printf("%d\r\n", c);
@@ -169,6 +288,9 @@ void editor_process_keypress() {
/*** init ***/
void init_editor() {
+ E.cx = 0;
+ E.cy = 0;
+
if (get_window_size(&E.screenrows, &E.screencols) == -1)
die("get_window_size");
}
README.md
@@ -2,7 +2,9 @@ https://viewsourcecode.org/snaptoken/kilo/
[ncurses][ncurses] is a library to supports a bunch of terminals.
[terminfo][terminfo] is a database to figure out the capabilities of a terminal.
+[VT100 User Guide][vt100]
[ncurses]: https://en.wikipedia.org/wiki/Ncurses
[terminfo]: https://en.wikipedia.org/wiki/Terminfo
+[vt100]: https://vt100.net/docs/vt100-ug/