#include "../include/terminal.h" #include #include "../include/data.h" #include "../include/define.h" #include #include #include #include #include "../include/buffer.h" #include "../include/split_screen.h" #include "../include/utf8.h" void die(const char* s) { write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3); lisp_shutdown(E.ctx); perror(s); exit(1); } void disableRawMode() { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) { die("tcsetattr"); } } void enableRawMode() { if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) { die("tcgetattr"); } struct termios raw = E.orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_oflag &= ~(OPOST); raw.c_cflag |= (CS8); raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); raw.c_cc[VMIN] = 0; raw.c_cc[VTIME] = 1; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { die("tcgetattr"); } } #include /* isprint */ char* keyToString(int key) { static char key_str[32]; if (key == '\r') { strcpy(key_str, "ENTER"); } else if (key == 0x09) { strcpy(key_str, "TAB"); } else if (key >= 1 && key <= 26) { snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1); } else { switch (key) { case ARROW_UP: strcpy(key_str, "ARROW-UP"); break; case ARROW_DOWN: strcpy(key_str, "ARROW-DOWN"); break; case ARROW_LEFT: strcpy(key_str, "ARROW-LEFT"); break; case ARROW_RIGHT: strcpy(key_str, "ARROW-RIGHT"); break; case PAGE_UP: strcpy(key_str, "PAGE-UP"); break; case PAGE_DOWN: strcpy(key_str, "PAGE-DOWN"); break; case DEL_KEY: strcpy(key_str, "DEL"); break; case BACKSPACE: strcpy(key_str, "BACKSPACE"); break; case BEG_LINE: strcpy(key_str, "HOME"); break; case END_LINE: strcpy(key_str, "END"); break; case '\x1b': strcpy(key_str, "ESCAPE"); break; default: if (key > 127) { /* UTF-8 code point — re-encode into the buffer */ char buf[5] = {0}; int n = utf8Encode((uint32_t)key, buf); snprintf(key_str, sizeof(key_str), "%.*s", n, buf); } else if (isprint(key)) { snprintf(key_str, sizeof(key_str), "%c", key); } else { snprintf(key_str, sizeof(key_str), "KEY-%d", key); } } } return key_str; } int editorReadKey() { char c; int nread; while (1) { fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); int max_fd = STDIN_FILENO; // Only watch wake pipe if LSP is ready AND active buffer is open EditorPane* active = splitScreenGetActivePane(); struct buffer_t* buf = active ? bufferFindById(active->buffer_id) : NULL; int lsp_active = E.lsp_client && E.lsp_client->wake_pipe[0] > 0 && E.lsp_client->state == LSP_READY && buf && buf->b_lsp_open; // ← only if this buffer is tracked if (lsp_active) { FD_SET(E.lsp_client->wake_pipe[0], &fds); if (E.lsp_client->wake_pipe[0] > max_fd) max_fd = E.lsp_client->wake_pipe[0]; } int ready = select(max_fd + 1, &fds, NULL, NULL, NULL); if (ready <= 0) continue; if (lsp_active && FD_ISSET(E.lsp_client->wake_pipe[0], &fds)) { char tmp[16]; read(E.lsp_client->wake_pipe[0], tmp, sizeof(tmp)); return LSP_WAKE_KEY; } if (FD_ISSET(STDIN_FILENO, &fds)) { nread = read(STDIN_FILENO, &c, 1); if (nread == 1) break; } } appDebug("f : %hhu %ld\r\n", c, 0x200); if (c == '\x1b') { char seq[6]; /* try to read escape sequence */ if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b'; if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b'; appDebug("f2 : %s\r\n", seq); 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 BEG_LINE; case '3': return DEL_KEY; case '4': return END_LINE; case '5': return PAGE_UP; case '6': return PAGE_DOWN; case '7': return BEG_LINE; case '8': return END_LINE; } } } 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 BEG_LINE; case 'F': return END_LINE; } } } return '\x1b'; } /* multi-byte UTF-8: read remaining bytes */ int seqlen = utf8Seqlen((unsigned char)c); if (seqlen > 1) { /* pack into a pseudo-codepoint just to pass bytes through; we handle encoding/decoding at the row level */ char buf[4] = {c, 0, 0, 0}; for (int i = 1; i < seqlen; i++) if (read(STDIN_FILENO, &buf[i], 1) != 1) break; /* decode and return as uint32, but we need int — use high range */ const char* p = buf; uint32_t cp = utf8Decode(&p); return (int)cp; /* caller re-encodes when inserting */ } return (unsigned char)c; } int getCursorPosition(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 getWindowSize(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 getCursorPosition(rows, cols); } *cols = ws.ws_col; *rows = ws.ws_row; return 0; } void appDebug(const char* fmt, ...) { #ifdef APP_DEBUG va_list ap; char message[1024]; va_start(ap, fmt); vsnprintf(message, 1024, fmt, ap); va_end(ap); fprintf(stderr, "%s\n", message); #endif }