io.c (6843B)
1 #include <stdlib.h> 2 #include <string.h> 3 #include <ctype.h> 4 #include <unistd.h> 5 #include <errno.h> 6 #include <fcntl.h> 7 8 #include "editor.h" 9 #include "term.h" 10 #include "vip.h" 11 #include "bar.h" 12 13 int read_key() 14 { 15 int nread; 16 char c; 17 while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { 18 if (nread == -1 && errno != EAGAIN) { 19 die("read"); 20 } 21 } 22 23 if (c == '\x1b') { 24 char seq[3]; 25 26 if (read(STDIN_FILENO, &seq[0], 1) != 1) 27 return '\x1b'; 28 if (read(STDIN_FILENO, &seq[1], 1) != 1) 29 return '\x1b'; 30 31 if (seq[0] == '[') { 32 if (seq[1] >= '0' && seq[1] <= '9') { 33 if (read(STDIN_FILENO, &seq[2], 1) != 1) 34 return '\x1b'; 35 if (seq[2] == '~') { 36 switch (seq[1]) { 37 case '1': return HOME_KEY; 38 case '3': return DEL_KEY; 39 case '4': return END_KEY; 40 case '5': return PAGE_UP; 41 case '6': return PAGE_DOWN; 42 case '7': return HOME_KEY; 43 case '8': return END_KEY; 44 } 45 } 46 } else { 47 switch (seq[1]) { 48 case 'A': return ARROW_UP; 49 case 'B': return ARROW_DOWN; 50 case 'C': return ARROW_RIGHT; 51 case 'D': return ARROW_LEFT; 52 case 'F': return END_KEY; 53 case 'H': return HOME_KEY; 54 } 55 } 56 } else if (seq[0] == 'O') { 57 switch (seq[1]) { 58 case 'F': return END_KEY; 59 case 'H': return HOME_KEY; 60 } 61 } 62 return '\x1b'; 63 } else { 64 return c; 65 } 66 } 67 68 void save_file() 69 { 70 if (vip.filename == NULL) { 71 vip.filename = prompt_editor("Save as: %s", NULL); 72 if (vip.filename == NULL) { 73 set_status_bar_message("Save aborted"); 74 return; 75 } 76 select_syntax_highlight(); 77 } 78 int len; 79 char *buf = rows_to_str(&len); 80 int fd = open(vip.filename, O_RDWR | O_CREAT, 0644); 81 if (fd != -1) { 82 if (ftruncate(fd, len) != -1) { 83 if (write(fd, buf, len) == len) { 84 close(fd); 85 free(buf); 86 vip.dirty = 0; 87 set_status_bar_message("\"%s\" %dL, %dB written", vip.filename, vip.rows, len); 88 return; 89 } 90 } 91 close(fd); 92 } 93 free(buf); 94 set_status_bar_message("Error saving: %s", strerror(errno)); 95 } 96 97 void process_key() 98 { 99 100 int c = read_key(); 101 switch (c) { 102 case '\r': 103 insert_new_line(); 104 break; 105 106 case HOME_KEY: 107 vip.cx = 0; 108 break; 109 110 case END_KEY: 111 if (vip.cy < vip.rows) { 112 vip.cx = vip.row[vip.cy].size; 113 } 114 break; 115 116 case BACKSPACE: 117 case CTRL_KEY('h'): 118 case DEL_KEY: /* PASSTHROUGH */ 119 if (vip.mode == INSERT) { 120 if (c == DEL_KEY) 121 move_cursor(ARROW_RIGHT); 122 del_char(); 123 } 124 break; 125 126 case PAGE_UP: 127 case PAGE_DOWN: 128 { 129 if (c == PAGE_UP) { 130 vip.cy = vip.rowoff; 131 } else if (c == PAGE_DOWN) { 132 vip.cy = vip.rowoff + vip.screenrows - 1; 133 if (vip.cy > vip.rows) vip.cy = vip.rows; 134 } 135 int times = vip.screenrows; 136 while (times--) 137 move_cursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); 138 } 139 break; 140 141 case ARROW_UP: 142 case ARROW_DOWN: 143 case ARROW_LEFT: 144 case ARROW_RIGHT: 145 if (vip.mode == INSERT) { 146 move_cursor(c); 147 } 148 break; 149 150 case CTRL_KEY('l'): 151 case '\x1b': 152 if (vip.mode == INSERT) { 153 vip.mode = NORMAL; 154 move_cursor(ARROW_LEFT); 155 } 156 break; 157 158 case 'i': /* PASSTHROUGH */ 159 if (vip.mode == NORMAL) { 160 vip.mode = INSERT; 161 break; 162 } 163 164 case ':': /* PASSTHROUGH */ 165 if (vip.mode == NORMAL) { 166 vip.mode = COMMAND; 167 char *cmd = prompt_editor(":%s", NULL); 168 if (cmd == NULL) { 169 vip.mode = NORMAL; 170 return; 171 } 172 switch (cmd[0]) { 173 case 'q': 174 if (cmd[1] == '!') { 175 write(STDOUT_FILENO, "\x1b[2J", 4); 176 write(STDOUT_FILENO, "\x1b[H", 3); 177 exit(0); 178 break; 179 } else { 180 if (vip.dirty) { 181 set_status_bar_message("No write since last change for buffer \"%s\"", vip.filename); 182 vip.mode = NORMAL; 183 return; 184 } 185 write(STDOUT_FILENO, "\x1b[2J", 4); 186 write(STDOUT_FILENO, "\x1b[H", 3); 187 exit(0); 188 break; 189 190 } 191 case 'w': 192 save_file(); 193 vip.mode = NORMAL; 194 } 195 } 196 case '/': /* PASSTHROUGH */ 197 if (vip.mode == NORMAL) { 198 find_editor(); 199 break; 200 } 201 202 case 'k': /* PASSTHROUGH */ 203 if (vip.mode != INSERT) { 204 move_cursor(ARROW_UP); 205 break; 206 } 207 208 case 'j': /* PASSTHROUGH */ 209 if (vip.mode != INSERT) { 210 move_cursor(ARROW_DOWN); 211 break; 212 } 213 214 case 'l': /* PASSTHROUGH */ 215 if (vip.mode != INSERT) { 216 move_cursor(ARROW_RIGHT); 217 break; 218 } 219 220 case 'h': /* PASSTHROUGH */ 221 if (vip.mode != INSERT) { 222 move_cursor(ARROW_LEFT); 223 break; 224 } 225 226 case 'o': /* PASSTHROUGH */ 227 if (vip.mode == NORMAL) { 228 shift_new_line(); 229 vip.mode = INSERT; 230 break; 231 } 232 233 case '0': /* PASSTHROUGH */ 234 if (vip.mode == NORMAL) { 235 vip.cx = 0; 236 break; 237 } 238 239 case '$': /* PASSTHROUGH */ 240 if (vip.mode == NORMAL) { 241 vip.cx = vip.row[vip.cy].size; 242 break; 243 } 244 245 default: 246 if (vip.mode == INSERT) { 247 insert_char(c); 248 } 249 break; 250 } 251 } 252 253 void draw_rows(struct abuf *ab) 254 { 255 for (int y = 0; y < vip.screenrows; y++) { 256 int filerow = y + vip.rowoff; 257 if (filerow >= vip.rows) { 258 if (vip.rows == 0 && y == vip.screenrows / 3) { 259 char welcome[11]; 260 int welcomelen = snprintf(welcome, sizeof(welcome), 261 "VIP v%s", VERSION); 262 if (welcomelen > vip.screencols) 263 welcomelen = vip.screencols; 264 int padding = (vip.screencols - welcomelen) / 2; 265 if (padding) { 266 abAppend(ab, "~", 1); 267 padding--; 268 } 269 while (padding--) abAppend(ab, " ", 1); 270 abAppend(ab, welcome, welcomelen); 271 } else { 272 abAppend(ab, "~", 1); 273 } 274 } else { 275 int len = vip.row[filerow].render_size - vip.coloff; 276 if (len < 0) len = 0; 277 if (len > vip.screencols) len = vip.screencols; 278 char *c = &vip.row[filerow].render[vip.coloff]; 279 unsigned char *hl = &vip.row[filerow].hl[vip.coloff]; 280 281 char *current_color = malloc(COLOR_LEN * 2); 282 int current_color_len = 0; 283 for (int j = 0; j < len; j++) { 284 if (iscntrl(c[j])) { 285 char sym = (c[j] <= 26) ? '@' + c[j] : '?'; 286 abAppend(ab, "\x1b[7m", 4); 287 abAppend(ab, &sym, 1); 288 abAppend(ab, "\x1b[m", 3); 289 if (strncmp(current_color, WHITE_BG, COLOR_LEN)) { 290 char *buf = malloc(COLOR_LEN * 2); 291 memcpy(buf, current_color, current_color_len); 292 abAppend(ab, buf, current_color_len); 293 free(buf); 294 } 295 } else if (hl[j] == NORMAL) { 296 if (strncmp(current_color, WHITE_BG, COLOR_LEN)) { 297 memcpy(current_color, WHITE_BG, COLOR_LEN); 298 current_color_len = COLOR_LEN; 299 abAppend(ab, WHITE_BG, COLOR_LEN); 300 } 301 abAppend(ab, &c[j], 1); 302 } else { 303 size_t len; 304 char *color = syntax_to_color(hl[j], &len); 305 if (strncmp(current_color, color, len)) { 306 memcpy(current_color, color, len); 307 current_color_len = len; 308 abAppend(ab, color, len); 309 } 310 free(color); 311 abAppend(ab, &c[j], 1); 312 } 313 } 314 free(current_color); 315 abAppend(ab, WHITE_BG, COLOR_LEN); 316 } 317 318 abAppend(ab, "\x1b[K", 3); 319 abAppend(ab, "\r\n", 2); 320 } 321 }