editor.c (5887B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <ctype.h> 6 7 #include "editor.h" 8 #include "vip.h" 9 #include "term.h" 10 #include "io.h" 11 #include "bar.h" 12 13 extern editor vip; 14 15 void refresh_screen() 16 { 17 scroll(); 18 19 struct abuf ab = ABUF_INIT; 20 21 abAppend(&ab, "\x1b[?25l", 6); 22 abAppend(&ab, "\x1b[H", 3); 23 24 draw_rows(&ab); 25 draw_status_bar(&ab); 26 draw_message_bar(&ab); 27 28 char buf[32]; 29 snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (vip.cy - vip.rowoff) + 1, 30 (vip.rx - vip.coloff) + 1); 31 abAppend(&ab, buf, strlen(buf)); 32 33 abAppend(&ab, "\x1b[?25h", 6); 34 35 write(STDOUT_FILENO, ab.b, ab.len); 36 abFree(&ab); 37 } 38 39 void move_cursor(int key) 40 { 41 row *row = (vip.cy >= vip.rows) ? NULL : &vip.row[vip.cy]; 42 switch (key) { 43 case ARROW_LEFT: 44 if (vip.cx != 0) { 45 vip.cx--; 46 } 47 break; 48 case ARROW_RIGHT: 49 if (row && vip.cx < row->size) { 50 vip.cx++; 51 } 52 break; 53 case ARROW_UP: 54 if (vip.cy != 0) { 55 vip.cy--; 56 } 57 break; 58 case ARROW_DOWN: 59 if (vip.cy < vip.rows) { 60 vip.cy++; 61 } 62 break; 63 } 64 row = (vip.cy >= vip.rows) ? NULL : &vip.row[vip.cy]; 65 int rowlen = row ? row->size : 0; 66 if (vip.cx > rowlen) { 67 vip.cx = rowlen; 68 } 69 } 70 71 void scroll() 72 { 73 vip.rx = 0; 74 if (vip.cy < vip.rows) { 75 vip.rx = row_cx_to_rx(&vip.row[vip.cy], vip.cx); 76 } 77 if (vip.cy < vip.rowoff) { 78 vip.rowoff = vip.cy; 79 } 80 if (vip.cy >= vip.rowoff + vip.screenrows) { 81 vip.rowoff = vip.cy - vip.screenrows + 1; 82 } 83 if (vip.rx < vip.coloff) { 84 vip.coloff = vip.rx; 85 } 86 if (vip.rx >= vip.coloff + vip.screencols) { 87 vip.coloff = vip.rx - vip.screencols + 1; 88 } 89 } 90 91 void insert_char(int c) 92 { 93 if (vip.cy == vip.rows) { 94 insert_row(vip.rows, "", 0); 95 } 96 row_insert_char(&vip.row[vip.cy], vip.cx, c); 97 vip.cx++; 98 } 99 100 void insert_new_line() 101 { 102 if (vip.cx == 0) { 103 insert_row(vip.cy, "", 0); 104 } else { 105 row *row = &vip.row[vip.cy]; 106 insert_row(vip.cy + 1, &row->chars[vip.cx], row->size - vip.cx); 107 row = &vip.row[vip.cy]; 108 row->size = vip.cx; 109 row->chars[row->size] = '\0'; 110 update_row(row); 111 } 112 vip.cy++; 113 vip.cx = 0; 114 } 115 116 /* 117 * 'o' in vim 118 */ 119 void shift_new_line() 120 { 121 insert_row(vip.cy + 1, "", 0); 122 vip.cy++; 123 vip.cx = 0; 124 } 125 126 void del_char() 127 { 128 if (vip.cy == vip.rows) return; 129 if (vip.cx == 0 && vip.cy == 0) return; 130 131 row *row = &vip.row[vip.cy]; 132 if (vip.cx > 0) { 133 row_del_char(row, vip.cx - 1); 134 vip.cx--; 135 } else { 136 vip.cx = vip.row[vip.cy - 1].size; 137 row_append_str(&vip.row[vip.cy - 1], row->chars, row->size); 138 del_row(vip.cy); 139 vip.cy--; 140 } 141 } 142 143 void init_editor() 144 { 145 vip.cx = 0; 146 vip.cy = 0; 147 vip.rx = 0; 148 vip.rowoff = 0; 149 vip.coloff = 0; 150 vip.rows = 0; 151 vip.row = NULL; 152 vip.dirty = 0; 153 vip.mode = NORMAL; 154 vip.filename = NULL; 155 vip.statusmsg[0] = '\0'; 156 vip.statusmsg_time = 0; 157 vip.syntax = NULL; 158 159 if (get_window_size(&vip.screenrows, &vip.screencols) == -1) { 160 die("get_window_size"); 161 } 162 vip.screenrows -= 2; 163 } 164 165 void open_editor(char *filename) 166 { 167 free(vip.filename); 168 vip.filename = strdup(filename); 169 170 select_syntax_highlight(); 171 172 FILE *fp = fopen(filename, "r"); 173 if (!fp) { 174 die("fopen"); 175 } 176 char *line = NULL; 177 size_t linecap = 0; 178 ssize_t len; 179 while ((len = getline(&line, &linecap, fp)) != -1) { 180 /* remove new line and carriage return at end of line */ 181 while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) { 182 len--; 183 } 184 insert_row(vip.rows, line, len); 185 } 186 free(line); 187 fclose(fp); 188 /* reset dirtiness as nothing is modified yet */ 189 vip.dirty = 0; 190 } 191 192 char *prompt_editor(char *prompt, void (*callback)(char *, int)) 193 { 194 size_t bufsize = 128; 195 char *buf = malloc(bufsize); 196 size_t buflen = 0; 197 buf[0] = '\0'; 198 while (1) { 199 set_status_bar_message(prompt, buf); 200 refresh_screen(); 201 int c = read_key(); 202 if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) { 203 if (buflen != 0) { 204 buf[--buflen] = '\0'; 205 } 206 } else if (c == '\x1b') { 207 set_status_bar_message(""); 208 if (callback) callback(buf, c); 209 free(buf); 210 return NULL; 211 } else if (c == '\r') { 212 if (buflen != 0) { 213 set_status_bar_message(""); 214 if (callback) callback(buf, c); 215 return buf; 216 } 217 } else if (!iscntrl(c) && c < 128) { 218 if (buflen == bufsize - 1) { 219 bufsize *= 2; 220 buf = realloc(buf, bufsize); 221 } 222 buf[buflen++] = c; 223 buf[buflen] = '\0'; 224 } 225 226 if (callback) callback(buf, c); 227 } 228 } 229 230 void find_callback(char *query, int key) 231 { 232 static int last_match = -1; 233 static int direction = 1; 234 235 static int saved_hl_line; 236 static char *saved_hl = NULL; 237 if (saved_hl) { 238 memcpy(vip.row[saved_hl_line].hl, saved_hl, vip.row[saved_hl_line].render_size); 239 free(saved_hl); 240 saved_hl = NULL; 241 } 242 243 if (key == '\r' || key == '\x1b') { 244 last_match = -1; 245 direction = 1; 246 return; 247 } else if (key == CTRL_KEY('n')) { 248 direction = 1; 249 } else if (key == CTRL_KEY('p')) { 250 direction = -1; 251 } else { 252 last_match = -1; 253 direction = 1; 254 } 255 256 if (last_match == -1) direction = 1; 257 int current = last_match; 258 for (int i = 0; i < vip.rows; i++) { 259 current += direction; 260 261 if (current == -1) current = vip.rows - 1; 262 else if (current == vip.rows) current = 0; 263 264 row *row = &vip.row[current]; 265 char *match = strstr(row->render, query); 266 if (match) { 267 last_match = current; 268 vip.cy = current; 269 vip.cx = row_rx_to_cx(row, match - row->render); 270 vip.rowoff = vip.rows; 271 272 saved_hl_line = current; 273 saved_hl = malloc(row->render_size); 274 memcpy(saved_hl, row->hl, row->render_size); 275 276 memset(&row->hl[match - row->render], MATCH, strlen(query)); 277 memset(&row->hl[match - row->render + strlen(query)], RESET, row->render_size - (match - row->render + strlen(query))); 278 break; 279 } 280 } 281 } 282 283 void find_editor() 284 { 285 int tmp_cx = vip.cx; 286 int tmp_cy = vip.cy; 287 int tmp_coloff = vip.coloff; 288 int tmp_rowoff = vip.rowoff; 289 char *query = prompt_editor("/%s", find_callback); 290 if (query) { 291 free(query); 292 } else { 293 vip.cx = tmp_cx; 294 vip.cy = tmp_cy; 295 vip.coloff = tmp_coloff; 296 vip.rowoff = tmp_rowoff; 297 } 298 }