vip

VI Plus
git clone https://codeberg.org/night0721/vip
Log | Files | Refs | README | LICENSE

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 }