Commit 5da2b92

mo khan <mo@mokhan.ca>
2021-07-26 02:38:00
add support for navigation
1 parent fe71aa1
Changed files (2)
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/