commit 50fbc03e203db9a961cd16a05e16dcac21c81ca5
parent 0dba0b4d55d6f6ae3d15bf4620738b89c234495a
Author: night0721 <[email protected]>
Date: Tue, 25 Jun 2024 01:10:38 +0100
add basic movement and ui
Diffstat:
M | vip.c | | | 292 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 290 insertions(+), 2 deletions(-)
diff --git a/vip.c b/vip.c
@@ -1,7 +1,295 @@
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
-int main(int argc, char **argv)
+#define 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
+};
+
+struct editor_config {
+ int cx, cy; /* cursor x, y*/
+ int screenrows, screencols;
+ struct termios orig_termios;
+};
+
+struct editor_config config;
+
+void die(const char *s)
+{
+ write(STDOUT_FILENO, "\x1b[2J", 4);
+ write(STDOUT_FILENO, "\x1b[H", 3);
+ perror(s);
+ exit(1);
+}
+
+void disable_raw_mode()
+{
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &config.orig_termios) == -1)
+ die("tcsetattr");
+}
+
+void enable_raw_mode()
+{
+ if (tcgetattr(STDIN_FILENO, &config.orig_termios) == -1)
+ die("tcgetattr");
+ atexit(disable_raw_mode);
+
+ struct termios raw = config.orig_termios;
+ /* disbable echo, line output and signals */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_cc[VMIN] = 0;
+ raw.c_cc[VTIME] = 1;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
+ die("tcsetattr");
+}
+
+int read_key()
+{
+ int nread;
+ char c;
+ while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
+ if (nread == -1 && errno != EAGAIN) die("read");
+ }
+ if (c == 'k') return ARROW_UP;
+ if (c == 'j') return ARROW_DOWN;
+ if (c == 'l') return ARROW_RIGHT;
+ if (c == 'h') return ARROW_LEFT;
+
+ 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 'F': return END_KEY;
+ case 'H': return HOME_KEY;
+ }
+ }
+ } else if (seq[0] == 'O') {
+ switch (seq[1]) {
+ case 'F': return END_KEY;
+ case 'H': return HOME_KEY;
+ }
+ }
+ return '\x1b';
+ } else {
+ return c;
+ }
+}
+
+int get_cursor_position(int *rows, int *cols)
+{
+ char buf[32];
+ unsigned int i = 0;
+ if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
+ return -1;
+ while (i < sizeof(buf) - 1) {
+ if (read(STDIN_FILENO, &buf[i], 1) != 1)
+ break;
+ if (buf[i] == 'R')
+ break;
+ i++;
+ }
+ buf[i] = '\0';
+ if (buf[0] != '\x1b' || buf[1] != '[')
+ return -1;
+ if (sscanf(&buf[2], "%d;%d", rows, cols) != 2)
+ return -1;
+ return 0;
+}
+
+int get_window_size(int *rows, int *cols)
+{
+ struct winsize ws;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1;
+ return get_cursor_position(rows, cols);
+ } else {
+ *cols = ws.ws_col;
+ *rows = ws.ws_row;
+ return 0;
+ }
+}
+
+struct abuf {
+ char *b;
+ int len;
+};
+
+#define ABUF_INIT {NULL, 0}
+
+void abAppend(struct abuf *ab, const char *s, int len)
+{
+ char *new = realloc(ab->b, ab->len + len);
+ if (new == NULL) return;
+ memcpy(&new[ab->len], s, len);
+ ab->b = new;
+ ab->len += len;
+}
+
+void abFree(struct abuf *ab)
{
- printf("vip 0.1\n");
+ free(ab->b);
+}
+
+void draw_rows(struct abuf *ab) {
+ for (int y = 0; y < config.screenrows; y++) {
+ if (y == config.screenrows / 3) {
+ char welcome[80];
+ int welcomelen = snprintf(welcome, sizeof(welcome),
+ "VIP v%s", VERSION);
+ if (welcomelen > config.screencols) welcomelen = config.screencols;
+ int padding = (config.screencols - welcomelen) / 2;
+ if (padding) {
+ abAppend(ab, "~", 1);
+ padding--;
+ }
+ while (padding--) abAppend(ab, " ", 1);
+ abAppend(ab, welcome, welcomelen);
+ } else {
+ abAppend(ab, "~", 1);
+ } abAppend(ab, "\x1b[K", 3);
+
+ if (y < config.screenrows - 1) {
+ abAppend(ab, "\r\n", 2);
+ }
+ }
+}
+
+void refresh_screen()
+{
+ struct abuf ab = ABUF_INIT;
+
+ abAppend(&ab, "\x1b[?25l", 6);
+ abAppend(&ab, "\x1b[H", 3);
+
+ draw_rows(&ab);
+
+ char buf[32];
+ snprintf(buf, sizeof(buf), "\x1b[%d;%dH", config.cy + 1, config.cx + 1);
+ abAppend(&ab, buf, strlen(buf));
+
+ abAppend(&ab, "\x1b[?25h", 6);
+
+ write(STDOUT_FILENO, ab.b, ab.len);
+ abFree(&ab);
+}
+
+void move_cursor(int key) {
+ switch (key) {
+ case ARROW_LEFT:
+ if (config.cx != 0) {
+ config.cx--;
+ }
+ break;
+ case ARROW_RIGHT:
+ if (config.cx != config.screencols - 1) {
+ config.cx++;
+ }
+ break;
+ case ARROW_UP:
+ if (config.cy != 0) {
+ config.cy--;
+ }
+ break;
+ case ARROW_DOWN:
+ if (config.cy != config.screenrows - 1) {
+ config.cy++;
+ }
+ break;
+ }
+}
+
+void process_key()
+{
+ int c = read_key();
+ switch (c) {
+ case CTRL_KEY('q'):
+ write(STDOUT_FILENO, "\x1b[2J", 4);
+ write(STDOUT_FILENO, "\x1b[H", 3);
+ exit(0);
+ break;
+
+ case HOME_KEY:
+ config.cx = 0;
+ break;
+ case END_KEY:
+ config.cx = config.screencols - 1;
+ break;
+ case PAGE_UP:
+ case PAGE_DOWN:
+ {
+ int times = config.screenrows;
+ while (times--)
+ move_cursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
+ }
+ break;
+
+ case ARROW_UP:
+ case ARROW_DOWN:
+ case ARROW_LEFT:
+ case ARROW_RIGHT:
+ move_cursor(c);
+ break;
+ }
+}
+
+void init_editor()
+{
+ config.cx = 0;
+ config.cy = 0;
+ if (get_window_size(&config.screenrows, &config.screencols) == -1)
+ die("get_window_size");
+}
+
+int main()
+{
+ enable_raw_mode();
+ init_editor();
+
+ while (1) {
+ refresh_screen();
+ process_key();
+ }
return 0;
}