0% found this document useful (0 votes)
150 views

Kilo Source Code Listing Scribd

Source code listing for Kilo text editor tutorial. https://viewsourcecode.org/snaptoken/kilo/ The text editor is antirez’s kilo, with some changes. It’s about 1000 lines of C in a single file with no dependencies, and it implements all the basic features you expect in a minimal editor, as well as syntax highlighting and a search feature.

Uploaded by

unni2003
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
150 views

Kilo Source Code Listing Scribd

Source code listing for Kilo text editor tutorial. https://viewsourcecode.org/snaptoken/kilo/ The text editor is antirez’s kilo, with some changes. It’s about 1000 lines of C in a single file with no dependencies, and it implements all the basic features you expect in a minimal editor, as well as syntax highlighting and a search feature.

Uploaded by

unni2003
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 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

You might also like