Вы находитесь на странице: 1из 18

kilo.

c
1 /*** includes ***/
2
3 #define _DEFAULT_SOURCE
4 #define _BSD_SOURCE
5 #define _GNU_SOURCE
6
7 #include <ctype.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/types.h>
16 #include <termios.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 /*** defines ***/
21
22 #define KILO_VERSION "0.0.1"
23 #define KILO_TAB_STOP 8
24 #define KILO_QUIT_TIMES 3
25
26 #define CTRL_KEY(k) ((k) & 0x1f)
27
28 enum editorKey {
29 BACKSPACE = 127,
30 ARROW_LEFT = 1000,
31 ARROW_RIGHT,
32 ARROW_UP,
33 ARROW_DOWN,
34 DEL_KEY,
35 HOME_KEY,
36 END_KEY,
37 PAGE_UP,
38 PAGE_DOWN
39 };
40
41 enum editorHighlight {
42 HL_NORMAL = 0,
43 HL_COMMENT,
44 HL_MLCOMMENT,
45 HL_KEYWORD1,
46 HL_KEYWORD2,
47 HL_STRING,
48 HL_NUMBER,
49 HL_MATCH
50 };
51
52 #define HL_HIGHLIGHT_NUMBERS (1<<0)
53 #define HL_HIGHLIGHT_STRINGS (1<<1)
54
55 /*** data ***/
56
57 struct editorSyntax {
58 char *filetype;
59 char **filematch;
60 char **keywords;
61 char *singleline_comment_start;
kilo.c
62 char *multiline_comment_start;
63 char *multiline_comment_end;
64 int flags;
65 };
66
67 typedef struct erow {
68 int idx;
69 int size;
70 int rsize;
71 char *chars;
72 char *render;
73 unsigned char *hl;
74 int hl_open_comment;
75 } erow;
76
77 struct editorConfig {
78 int cx, cy;
79 int rx;
80 int rowoff;
81 int coloff;
82 int screenrows;
83 int screencols;
84 int numrows;
85 erow *row;
86 int dirty;
87 char *filename;
88 char statusmsg[80];
89 time_t statusmsg_time;
90 struct editorSyntax *syntax;
91 struct termios orig_termios;
92 };
93
94 struct editorConfig E;
95
96 /*** filetypes ***/
97
98 char *C_HL_extensions[] = { ".c", ".h", ".cpp", NULL };
99 char *C_HL_keywords[] = {
100 "switch", "if", "while", "for", "break", "continue", "return", "else",
101 "struct", "union", "typedef", "static", "enum", "class", "case",
102
103 "int|", "long|", "double|", "float|", "char|", "unsigned|", "signed|",
104 "void|", NULL
105 };
106
107 struct editorSyntax HLDB[] = {
108 {
109 "c",
110 C_HL_extensions,
111 C_HL_keywords,
112 "//", "/*", "*/",
113 HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
114 },
115 };
116
117 #define HLDB_ENTRIES (sizeof(HLDB) / sizeof(HLDB[0]))
118
119 /*** prototypes ***/
120
121 void editorSetStatusMessage(const char *fmt, ...);
122 void editorRefreshScreen();
kilo.c
123 char *editorPrompt(char *prompt, void (*callback)(char *, int));
124
125 /*** terminal ***/
126
127 void die(const char *s) {
128 write(STDOUT_FILENO, "\x1b[2J", 4);
129 write(STDOUT_FILENO, "\x1b[H", 3);
130
131 perror(s);
132 exit(1);
133 }
134
135 void disableRawMode() {
136 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
137 die("tcsetattr");
138 }
139
140 void enableRawMode() {
141 if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr");
142 atexit(disableRawMode);
143
144 struct termios raw = E.orig_termios;
145 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
146 raw.c_oflag &= ~(OPOST);
147 raw.c_cflag |= (CS8);
148 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
149 raw.c_cc[VMIN] = 0;
150 raw.c_cc[VTIME] = 1;
151
152 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr");
153 }
154
155 int editorReadKey() {
156 int nread;
157 char c;
158 while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
159 if (nread == -1 && errno != EAGAIN) die("read");
160 }
161
162 if (c == '\x1b') {
163 char seq[3];
164
165 if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b';
166 if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b';
167
168 if (seq[0] == '[') {
169 if (seq[1] >= '0' && seq[1] <= '9') {
170 if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b';
171 if (seq[2] == '~') {
172 switch (seq[1]) {
173 case '1': return HOME_KEY;
174 case '3': return DEL_KEY;
175 case '4': return END_KEY;
176 case '5': return PAGE_UP;
177 case '6': return PAGE_DOWN;
178 case '7': return HOME_KEY;
179 case '8': return END_KEY;
180 }
181 }
182 } else {
183 switch (seq[1]) {
kilo.c
184 case 'A': return ARROW_UP;
185 case 'B': return ARROW_DOWN;
186 case 'C': return ARROW_RIGHT;
187 case 'D': return ARROW_LEFT;
188 case 'H': return HOME_KEY;
189 case 'F': return END_KEY;
190 }
191 }
192 } else if (seq[0] == 'O') {
193 switch (seq[1]) {
194 case 'H': return HOME_KEY;
195 case 'F': return END_KEY;
196 }
197 }
198
199 return '\x1b';
200 } else {
201 return c;
202 }
203 }
204
205 int getCursorPosition(int *rows, int *cols) {
206 char buf[32];
207 unsigned int i = 0;
208
209 if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
210
211 while (i < sizeof(buf) - 1) {
212 if (read(STDIN_FILENO, &buf[i], 1) != 1) break;
213 if (buf[i] == 'R') break;
214 i++;
215 }
216 buf[i] = '\0';
217
218 if (buf[0] != '\x1b' || buf[1] != '[') return -1;
219 if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1;
220
221 return 0;
222 }
223
224 int getWindowSize(int *rows, int *cols) {
225 struct winsize ws;
226
227 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
228 if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1;
229 return getCursorPosition(rows, cols);
230 } else {
231 *cols = ws.ws_col;
232 *rows = ws.ws_row;
233 return 0;
234 }
235 }
236
237 /*** syntax highlighting ***/
238
239 int is_separator(int c) {
240 return isspace(c) || c == '\0' || strchr(",.()+-/*=~%<>[];", c) != NULL;
241 }
242
243 void editorUpdateSyntax(erow *row) {
244 row->hl = realloc(row->hl, row->rsize);
kilo.c
245 memset(row->hl, HL_NORMAL, row->rsize);
246
247 if (E.syntax == NULL) return;
248
249 char **keywords = E.syntax->keywords;
250
251 char *scs = E.syntax->singleline_comment_start;
252 char *mcs = E.syntax->multiline_comment_start;
253 char *mce = E.syntax->multiline_comment_end;
254
255 int scs_len = scs ? strlen(scs) : 0;
256 int mcs_len = mcs ? strlen(mcs) : 0;
257 int mce_len = mce ? strlen(mce) : 0;
258
259 int prev_sep = 1;
260 int in_string = 0;
261 int in_comment = (row->idx > 0 && E.row[row->idx - 1].hl_open_comment);
262
263 int i = 0;
264 while (i < row->rsize) {
265 char c = row->render[i];
266 unsigned char prev_hl = (i > 0) ? row->hl[i - 1] : HL_NORMAL;
267
268 if (scs_len && !in_string && !in_comment) {
269 if (!strncmp(&row->render[i], scs, scs_len)) {
270 memset(&row->hl[i], HL_COMMENT, row->rsize - i);
271 break;
272 }
273 }
274
275 if (mcs_len && mce_len && !in_string) {
276 if (in_comment) {
277 row->hl[i] = HL_MLCOMMENT;
278 if (!strncmp(&row->render[i], mce, mce_len)) {
279 memset(&row->hl[i], HL_MLCOMMENT, mce_len);
280 i += mce_len;
281 in_comment = 0;
282 prev_sep = 1;
283 continue;
284 } else {
285 i++;
286 continue;
287 }
288 } else if (!strncmp(&row->render[i], mcs, mcs_len)) {
289 memset(&row->hl[i], HL_MLCOMMENT, mcs_len);
290 i += mcs_len;
291 in_comment = 1;
292 continue;
293 }
294 }
295
296 if (E.syntax->flags & HL_HIGHLIGHT_STRINGS) {
297 if (in_string) {
298 row->hl[i] = HL_STRING;
299 if (c == '\\' && i + 1 < row->rsize) {
300 row->hl[i + 1] = HL_STRING;
301 i += 2;
302 continue;
303 }
304 if (c == in_string) in_string = 0;
305 i++;
kilo.c
306 prev_sep = 1;
307 continue;
308 } else {
309 if (c == '"' || c == '\'') {
310 in_string = c;
311 row->hl[i] = HL_STRING;
312 i++;
313 continue;
314 }
315 }
316 }
317
318 if (E.syntax->flags & HL_HIGHLIGHT_NUMBERS) {
319 if ((isdigit(c) && (prev_sep || prev_hl == HL_NUMBER)) ||
320 (c == '.' && prev_hl == HL_NUMBER)) {
321 row->hl[i] = HL_NUMBER;
322 i++;
323 prev_sep = 0;
324 continue;
325 }
326 }
327
328 if (prev_sep) {
329 int j;
330 for (j = 0; keywords[j]; j++) {
331 int klen = strlen(keywords[j]);
332 int kw2 = keywords[j][klen - 1] == '|';
333 if (kw2) klen--;
334
335 if (!strncmp(&row->render[i], keywords[j], klen) &&
336 is_separator(row->render[i + klen])) {
337 memset(&row->hl[i], kw2 ? HL_KEYWORD2 : HL_KEYWORD1, klen);
338 i += klen;
339 break;
340 }
341 }
342 if (keywords[j] != NULL) {
343 prev_sep = 0;
344 continue;
345 }
346 }
347
348 prev_sep = is_separator(c);
349 i++;
350 }
351
352 int changed = (row->hl_open_comment != in_comment);
353 row->hl_open_comment = in_comment;
354 if (changed && row->idx + 1 < E.numrows)
355 editorUpdateSyntax(&E.row[row->idx + 1]);
356 }
357
358 int editorSyntaxToColor(int hl) {
359 switch (hl) {
360 case HL_COMMENT:
361 case HL_MLCOMMENT: return 36;
362 case HL_KEYWORD1: return 33;
363 case HL_KEYWORD2: return 32;
364 case HL_STRING: return 35;
365 case HL_NUMBER: return 31;
366 case HL_MATCH: return 34;
kilo.c
367 default: return 37;
368 }
369 }
370
371 void editorSelectSyntaxHighlight() {
372 E.syntax = NULL;
373 if (E.filename == NULL) return;
374
375 char *ext = strrchr(E.filename, '.');
376
377 for (unsigned int j = 0; j < HLDB_ENTRIES; j++) {
378 struct editorSyntax *s = &HLDB[j];
379 unsigned int i = 0;
380 while (s->filematch[i]) {
381 int is_ext = (s->filematch[i][0] == '.');
382 if ((is_ext && ext && !strcmp(ext, s->filematch[i])) ||
383 (!is_ext && strstr(E.filename, s->filematch[i]))) {
384 E.syntax = s;
385
386 int filerow;
387 for (filerow = 0; filerow < E.numrows; filerow++) {
388 editorUpdateSyntax(&E.row[filerow]);
389 }
390
391 return;
392 }
393 i++;
394 }
395 }
396 }
397
398 /*** row operations ***/
399
400 int editorRowCxToRx(erow *row, int cx) {
401 int rx = 0;
402 int j;
403 for (j = 0; j < cx; j++) {
404 if (row->chars[j] == '\t')
405 rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP);
406 rx++;
407 }
408 return rx;
409 }
410
411 int editorRowRxToCx(erow *row, int rx) {
412 int cur_rx = 0;
413 int cx;
414 for (cx = 0; cx < row->size; cx++) {
415 if (row->chars[cx] == '\t')
416 cur_rx += (KILO_TAB_STOP - 1) - (cur_rx % KILO_TAB_STOP);
417 cur_rx++;
418
419 if (cur_rx > rx) return cx;
420 }
421 return cx;
422 }
423
424 void editorUpdateRow(erow *row) {
425 int tabs = 0;
426 int j;
427 for (j = 0; j < row->size; j++)
kilo.c
428 if (row->chars[j] == '\t') tabs++;
429
430 free(row->render);
431 row->render = malloc(row->size + tabs*(KILO_TAB_STOP - 1) + 1);
432
433 int idx = 0;
434 for (j = 0; j < row->size; j++) {
435 if (row->chars[j] == '\t') {
436 row->render[idx++] = ' ';
437 while (idx % KILO_TAB_STOP != 0) row->render[idx++] = ' ';
438 } else {
439 row->render[idx++] = row->chars[j];
440 }
441 }
442 row->render[idx] = '\0';
443 row->rsize = idx;
444
445 editorUpdateSyntax(row);
446 }
447
448 void editorInsertRow(int at, char *s, size_t len) {
449 if (at < 0 || at > E.numrows) return;
450
451 E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
452 memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
453 for (int j = at + 1; j <= E.numrows; j++) E.row[j].idx++;
454
455 E.row[at].idx = at;
456
457 E.row[at].size = len;
458 E.row[at].chars = malloc(len + 1);
459 memcpy(E.row[at].chars, s, len);
460 E.row[at].chars[len] = '\0';
461
462 E.row[at].rsize = 0;
463 E.row[at].render = NULL;
464 E.row[at].hl = NULL;
465 E.row[at].hl_open_comment = 0;
466 editorUpdateRow(&E.row[at]);
467
468 E.numrows++;
469 E.dirty++;
470 }
471
472 void editorFreeRow(erow *row) {
473 free(row->render);
474 free(row->chars);
475 free(row->hl);
476 }
477
478 void editorDelRow(int at) {
479 if (at < 0 || at >= E.numrows) return;
480 editorFreeRow(&E.row[at]);
481 memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
482 for (int j = at; j < E.numrows - 1; j++) E.row[j].idx--;
483 E.numrows--;
484 E.dirty++;
485 }
486
487 void editorRowInsertChar(erow *row, int at, int c) {
488 if (at < 0 || at > row->size) at = row->size;
kilo.c
489 row->chars = realloc(row->chars, row->size + 2);
490 memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
491 row->size++;
492 row->chars[at] = c;
493 editorUpdateRow(row);
494 E.dirty++;
495 }
496
497 void editorRowAppendString(erow *row, char *s, size_t len) {
498 row->chars = realloc(row->chars, row->size + len + 1);
499 memcpy(&row->chars[row->size], s, len);
500 row->size += len;
501 row->chars[row->size] = '\0';
502 editorUpdateRow(row);
503 E.dirty++;
504 }
505
506 void editorRowDelChar(erow *row, int at) {
507 if (at < 0 || at >= row->size) return;
508 memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
509 row->size--;
510 editorUpdateRow(row);
511 E.dirty++;
512 }
513
514 /*** editor operations ***/
515
516 void editorInsertChar(int c) {
517 if (E.cy == E.numrows) {
518 editorInsertRow(E.numrows, "", 0);
519 }
520 editorRowInsertChar(&E.row[E.cy], E.cx, c);
521 E.cx++;
522 }
523
524 void editorInsertNewline() {
525 if (E.cx == 0) {
526 editorInsertRow(E.cy, "", 0);
527 } else {
528 erow *row = &E.row[E.cy];
529 editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx);
530 row = &E.row[E.cy];
531 row->size = E.cx;
532 row->chars[row->size] = '\0';
533 editorUpdateRow(row);
534 }
535 E.cy++;
536 E.cx = 0;
537 }
538
539 void editorDelChar() {
540 if (E.cy == E.numrows) return;
541 if (E.cx == 0 && E.cy == 0) return;
542
543 erow *row = &E.row[E.cy];
544 if (E.cx > 0) {
545 editorRowDelChar(row, E.cx - 1);
546 E.cx--;
547 } else {
548 E.cx = E.row[E.cy - 1].size;
549 editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size);
kilo.c
550 editorDelRow(E.cy);
551 E.cy--;
552 }
553 }
554
555 /*** file i/o ***/
556
557 char *editorRowsToString(int *buflen) {
558 int totlen = 0;
559 int j;
560 for (j = 0; j < E.numrows; j++)
561 totlen += E.row[j].size + 1;
562 *buflen = totlen;
563
564 char *buf = malloc(totlen);
565 char *p = buf;
566 for (j = 0; j < E.numrows; j++) {
567 memcpy(p, E.row[j].chars, E.row[j].size);
568 p += E.row[j].size;
569 *p = '\n';
570 p++;
571 }
572
573 return buf;
574 }
575
576 void editorOpen(char *filename) {
577 free(E.filename);
578 E.filename = strdup(filename);
579
580 editorSelectSyntaxHighlight();
581
582 FILE *fp = fopen(filename, "r");
583 if (!fp) die("fopen");
584
585 char *line = NULL;
586 size_t linecap = 0;
587 ssize_t linelen;
588 while ((linelen = getline(&line, &linecap, fp)) != -1) {
589 while (linelen > 0 && (line[linelen - 1] == '\n' ||
590 line[linelen - 1] == '\r'))
591 linelen--;
592 editorInsertRow(E.numrows, line, linelen);
593 }
594 free(line);
595 fclose(fp);
596 E.dirty = 0;
597 }
598
599 void editorSave() {
600 if (E.filename == NULL) {
601 E.filename = editorPrompt("Save as: %s (ESC to cancel)", NULL);
602 if (E.filename == NULL) {
603 editorSetStatusMessage("Save aborted");
604 return;
605 }
606 editorSelectSyntaxHighlight();
607 }
608
609 int len;
610 char *buf = editorRowsToString(&len);
kilo.c
611
612 int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
613 if (fd != -1) {
614 if (ftruncate(fd, len) != -1) {
615 if (write(fd, buf, len) == len) {
616 close(fd);
617 free(buf);
618 E.dirty = 0;
619 editorSetStatusMessage("%d bytes written to disk", len);
620 return;
621 }
622 }
623 close(fd);
624 }
625
626 free(buf);
627 editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
628 }
629
630 /*** find ***/
631
632 void editorFindCallback(char *query, int key) {
633 static int last_match = -1;
634 static int direction = 1;
635
636 static int saved_hl_line;
637 static char *saved_hl = NULL;
638
639 if (saved_hl) {
640 memcpy(E.row[saved_hl_line].hl, saved_hl, E.row[saved_hl_line].rsize);
641 free(saved_hl);
642 saved_hl = NULL;
643 }
644
645 if (key == '\r' || key == '\x1b') {
646 last_match = -1;
647 direction = 1;
648 return;
649 } else if (key == ARROW_RIGHT || key == ARROW_DOWN) {
650 direction = 1;
651 } else if (key == ARROW_LEFT || key == ARROW_UP) {
652 direction = -1;
653 } else {
654 last_match = -1;
655 direction = 1;
656 }
657
658 if (last_match == -1) direction = 1;
659 int current = last_match;
660 int i;
661 for (i = 0; i < E.numrows; i++) {
662 current += direction;
663 if (current == -1) current = E.numrows - 1;
664 else if (current == E.numrows) current = 0;
665
666 erow *row = &E.row[current];
667 char *match = strstr(row->render, query);
668 if (match) {
669 last_match = current;
670 E.cy = current;
671 E.cx = editorRowRxToCx(row, match - row->render);
kilo.c
672 E.rowoff = E.numrows;
673
674 saved_hl_line = current;
675 saved_hl = malloc(row->rsize);
676 memcpy(saved_hl, row->hl, row->rsize);
677 memset(&row->hl[match - row->render], HL_MATCH, strlen(query));
678 break;
679 }
680 }
681 }
682
683 void editorFind() {
684 int saved_cx = E.cx;
685 int saved_cy = E.cy;
686 int saved_coloff = E.coloff;
687 int saved_rowoff = E.rowoff;
688
689 char *query = editorPrompt("Search: %s (Use ESC/Arrows/Enter)",
690 editorFindCallback);
691
692 if (query) {
693 free(query);
694 } else {
695 E.cx = saved_cx;
696 E.cy = saved_cy;
697 E.coloff = saved_coloff;
698 E.rowoff = saved_rowoff;
699 }
700 }
701
702 /*** append buffer ***/
703
704 struct abuf {
705 char *b;
706 int len;
707 };
708
709 #define ABUF_INIT {NULL, 0}
710
711 void abAppend(struct abuf *ab, const char *s, int len) {
712 char *new = realloc(ab->b, ab->len + len);
713
714 if (new == NULL) return;
715 memcpy(&new[ab->len], s, len);
716 ab->b = new;
717 ab->len += len;
718 }
719
720 void abFree(struct abuf *ab) {
721 free(ab->b);
722 }
723
724 /*** output ***/
725
726 void editorScroll() {
727 E.rx = 0;
728 if (E.cy < E.numrows) {
729 E.rx = editorRowCxToRx(&E.row[E.cy], E.cx);
730 }
731
732 if (E.cy < E.rowoff) {
kilo.c
733 E.rowoff = E.cy;
734 }
735 if (E.cy >= E.rowoff + E.screenrows) {
736 E.rowoff = E.cy - E.screenrows + 1;
737 }
738 if (E.rx < E.coloff) {
739 E.coloff = E.rx;
740 }
741 if (E.rx >= E.coloff + E.screencols) {
742 E.coloff = E.rx - E.screencols + 1;
743 }
744 }
745
746 void editorDrawRows(struct abuf *ab) {
747 int y;
748 for (y = 0; y < E.screenrows; y++) {
749 int filerow = y + E.rowoff;
750 if (filerow >= E.numrows) {
751 if (E.numrows == 0 && y == E.screenrows / 3) {
752 char welcome[80];
753 int welcomelen = snprintf(welcome, sizeof(welcome),
754 "Kilo editor -- version %s", KILO_VERSION);
755 if (welcomelen > E.screencols) welcomelen = E.screencols;
756 int padding = (E.screencols - welcomelen) / 2;
757 if (padding) {
758 abAppend(ab, "~", 1);
759 padding--;
760 }
761 while (padding--) abAppend(ab, " ", 1);
762 abAppend(ab, welcome, welcomelen);
763 } else {
764 abAppend(ab, "~", 1);
765 }
766 } else {
767 int len = E.row[filerow].rsize - E.coloff;
768 if (len < 0) len = 0;
769 if (len > E.screencols) len = E.screencols;
770 char *c = &E.row[filerow].render[E.coloff];
771 unsigned char *hl = &E.row[filerow].hl[E.coloff];
772 int current_color = -1;
773 int j;
774 for (j = 0; j < len; j++) {
775 if (iscntrl(c[j])) {
776 char sym = (c[j] <= 26) ? '@' + c[j] : '?';
777 abAppend(ab, "\x1b[7m", 4);
778 abAppend(ab, &sym, 1);
779 abAppend(ab, "\x1b[m", 3);
780 if (current_color != -1) {
781 char buf[16];
782 int clen = snprintf(buf, sizeof(buf), "\x1b[%dm", current_color);
783 abAppend(ab, buf, clen);
784 }
785 } else if (hl[j] == HL_NORMAL) {
786 if (current_color != -1) {
787 abAppend(ab, "\x1b[39m", 5);
788 current_color = -1;
789 }
790 abAppend(ab, &c[j], 1);
791 } else {
792 int color = editorSyntaxToColor(hl[j]);
793 if (color != current_color) {
kilo.c
794 current_color = color;
795 char buf[16];
796 int clen = snprintf(buf, sizeof(buf), "\x1b[%dm", color);
797 abAppend(ab, buf, clen);
798 }
799 abAppend(ab, &c[j], 1);
800 }
801 }
802 abAppend(ab, "\x1b[39m", 5);
803 }
804
805 abAppend(ab, "\x1b[K", 3);
806 abAppend(ab, "\r\n", 2);
807 }
808 }
809
810 void editorDrawStatusBar(struct abuf *ab) {
811 abAppend(ab, "\x1b[7m", 4);
812 char status[80], rstatus[80];
813 int len = snprintf(status, sizeof(status), "%.20s - %d lines %s",
814 E.filename ? E.filename : "[No Name]", E.numrows,
815 E.dirty ? "(modified)" : "");
816 int rlen = snprintf(rstatus, sizeof(rstatus), "%s | %d/%d",
817 E.syntax ? E.syntax->filetype : "no ft", E.cy + 1, E.numrows);
818 if (len > E.screencols) len = E.screencols;
819 abAppend(ab, status, len);
820 while (len < E.screencols) {
821 if (E.screencols - len == rlen) {
822 abAppend(ab, rstatus, rlen);
823 break;
824 } else {
825 abAppend(ab, " ", 1);
826 len++;
827 }
828 }
829 abAppend(ab, "\x1b[m", 3);
830 abAppend(ab, "\r\n", 2);
831 }
832
833 void editorDrawMessageBar(struct abuf *ab) {
834 abAppend(ab, "\x1b[K", 3);
835 int msglen = strlen(E.statusmsg);
836 if (msglen > E.screencols) msglen = E.screencols;
837 if (msglen && time(NULL) - E.statusmsg_time < 5)
838 abAppend(ab, E.statusmsg, msglen);
839 }
840
841 void editorRefreshScreen() {
842 editorScroll();
843
844 struct abuf ab = ABUF_INIT;
845
846 abAppend(&ab, "\x1b[?25l", 6);
847 abAppend(&ab, "\x1b[H", 3);
848
849 editorDrawRows(&ab);
850 editorDrawStatusBar(&ab);
851 editorDrawMessageBar(&ab);
852
853 char buf[32];
854 snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1,
kilo.c
855 (E.rx - E.coloff) + 1);
856 abAppend(&ab, buf, strlen(buf));
857
858 abAppend(&ab, "\x1b[?25h", 6);
859
860 write(STDOUT_FILENO, ab.b, ab.len);
861 abFree(&ab);
862 }
863
864 void editorSetStatusMessage(const char *fmt, ...) {
865 va_list ap;
866 va_start(ap, fmt);
867 vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap);
868 va_end(ap);
869 E.statusmsg_time = time(NULL);
870 }
871
872 /*** input ***/
873
874 char *editorPrompt(char *prompt, void (*callback)(char *, int)) {
875 size_t bufsize = 128;
876 char *buf = malloc(bufsize);
877
878 size_t buflen = 0;
879 buf[0] = '\0';
880
881 while (1) {
882 editorSetStatusMessage(prompt, buf);
883 editorRefreshScreen();
884
885 int c = editorReadKey();
886 if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
887 if (buflen != 0) buf[--buflen] = '\0';
888 } else if (c == '\x1b') {
889 editorSetStatusMessage("");
890 if (callback) callback(buf, c);
891 free(buf);
892 return NULL;
893 } else if (c == '\r') {
894 if (buflen != 0) {
895 editorSetStatusMessage("");
896 if (callback) callback(buf, c);
897 return buf;
898 }
899 } else if (!iscntrl(c) && c < 128) {
900 if (buflen == bufsize - 1) {
901 bufsize *= 2;
902 buf = realloc(buf, bufsize);
903 }
904 buf[buflen++] = c;
905 buf[buflen] = '\0';
906 }
907
908 if (callback) callback(buf, c);
909 }
910 }
911
912 void editorMoveCursor(int key) {
913 erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
914
915 switch (key) {
kilo.c
916 case ARROW_LEFT:
917 if (E.cx != 0) {
918 E.cx--;
919 } else if (E.cy > 0) {
920 E.cy--;
921 E.cx = E.row[E.cy].size;
922 }
923 break;
924 case ARROW_RIGHT:
925 if (row && E.cx < row->size) {
926 E.cx++;
927 } else if (row && E.cx == row->size) {
928 E.cy++;
929 E.cx = 0;
930 }
931 break;
932 case ARROW_UP:
933 if (E.cy != 0) {
934 E.cy--;
935 }
936 break;
937 case ARROW_DOWN:
938 if (E.cy < E.numrows) {
939 E.cy++;
940 }
941 break;
942 }
943
944 row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
945 int rowlen = row ? row->size : 0;
946 if (E.cx > rowlen) {
947 E.cx = rowlen;
948 }
949 }
950
951 void editorProcessKeypress() {
952 static int quit_times = KILO_QUIT_TIMES;
953
954 int c = editorReadKey();
955
956 switch (c) {
957 case '\r':
958 editorInsertNewline();
959 break;
960
961 case CTRL_KEY('q'):
962 if (E.dirty && quit_times > 0) {
963 editorSetStatusMessage("WARNING!!! File has unsaved changes. "
964 "Press Ctrl-Q %d more times to quit.", quit_times);
965 quit_times--;
966 return;
967 }
968 write(STDOUT_FILENO, "\x1b[2J", 4);
969 write(STDOUT_FILENO, "\x1b[H", 3);
970 exit(0);
971 break;
972
973 case CTRL_KEY('s'):
974 editorSave();
975 break;
976
kilo.c
977 case HOME_KEY:
978 E.cx = 0;
979 break;
980
981 case END_KEY:
982 if (E.cy < E.numrows)
983 E.cx = E.row[E.cy].size;
984 break;
985
986 case CTRL_KEY('f'):
987 editorFind();
988 break;
989
990 case BACKSPACE:
991 case CTRL_KEY('h'):
992 case DEL_KEY:
993 if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT);
994 editorDelChar();
995 break;
996
997 case PAGE_UP:
998 case PAGE_DOWN:
999 {
1000 if (c == PAGE_UP) {
1001 E.cy = E.rowoff;
1002 } else if (c == PAGE_DOWN) {
1003 E.cy = E.rowoff + E.screenrows - 1;
1004 if (E.cy > E.numrows) E.cy = E.numrows;
1005 }
1006
1007 int times = E.screenrows;
1008 while (times--)
1009 editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
1010 }
1011 break;
1012
1013 case ARROW_UP:
1014 case ARROW_DOWN:
1015 case ARROW_LEFT:
1016 case ARROW_RIGHT:
1017 editorMoveCursor(c);
1018 break;
1019
1020 case CTRL_KEY('l'):
1021 case '\x1b':
1022 break;
1023
1024 default:
1025 editorInsertChar(c);
1026 break;
1027 }
1028
1029 quit_times = KILO_QUIT_TIMES;
1030 }
1031
1032 /*** init ***/
1033
1034 void initEditor() {
1035 E.cx = 0;
1036 E.cy = 0;
1037 E.rx = 0;
kilo.c
1038 E.rowoff = 0;
1039 E.coloff = 0;
1040 E.numrows = 0;
1041 E.row = NULL;
1042 E.dirty = 0;
1043 E.filename = NULL;
1044 E.statusmsg[0] = '\0';
1045 E.statusmsg_time = 0;
1046 E.syntax = NULL;
1047
1048 if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
1049 E.screenrows -= 2;
1050 }
1051
1052 int main(int argc, char *argv[]) {
1053 enableRawMode();
1054 initEditor();
1055 if (argc >= 2) {
1056 editorOpen(argv[1]);
1057 }
1058
1059 editorSetStatusMessage(
1060 "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find");
1061
1062 while (1) {
1063 editorRefreshScreen();
1064 editorProcessKeypress();
1065 }
1066
1067 return 0;
1068 }
1069

Вам также может понравиться