Introduction
Introduction Statistics Contact Development Disclaimer Help
tve.c - ve - a minimal text editor (work in progress)
git clone git://src.adamsgaard.dk/ve
Log
Files
Refs
README
LICENSE
---
tve.c (24211B)
---
1 /* see LICENSE for license details */
2
3 #include <sys/types.h>
4 #include <sys/ioctl.h>
5
6 #include <ctype.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <termios.h>
15 #include <time.h>
16 #include <unistd.h>
17
18 #include "arg.h"
19
20 /* MACROS */
21 #define ABUF_INIT {NULL, 0}
22 #define CTRL_KEY(k) ((k) & 0x1f)
23
24
25 /* TYPES */
26 struct abuf {
27 char *b;
28 int len;
29 };
30
31 /* editor row: stores a row of text */
32 typedef struct eRow {
33 char *chars; /* text read from file */
34 char *rchars; /* text to render */
35 int size; /* length of chars */
36 int rsize; /* length of rchars */
37 unsigned char *hl; /* array of rsize storing highlight type p…
38 } eRow;
39
40 struct editor_config {
41 int cursor_x, cursor_y, cursor_rx;
42 int screen_rows, screen_columns;
43 int num_rows;
44 eRow *row;
45 int row_offset, column_offset;
46 char *filename;
47 struct termios orig_termios;
48 int mode; /* 0: normal, 1: insert, 2: visual */
49 int show_status;
50 char status_msg[80];
51 time_t status_msg_time;
52 int file_changed;
53 char *find_query;
54 int find_direction;
55 };
56
57 /* color scheme types */
58 enum { ColorNormal, ColorDigit, ColorComment, ColorKeyword };
59 enum { FG, BG, STYLE };
60
61 /* configuration, allows nested code to access above variables */
62 #include "config.h"
63
64
65 /* FUNCTION DECLARATIONS */
66 char* editor_prompt(char *prompt);
67 void editor_set_status_message(const char *fmt, ...);
68
69
70 /* GLOBAL VARIABLES */
71 struct editor_config E;
72 char *argv0;
73
74 /* FUNCTION DEFINITIONS */
75
76 /** TERMINAL **/
77 void
78 die(const char *s)
79 {
80 /* clear screen on exit */
81 write(STDOUT_FILENO, "\x1b[2J", 4);
82 write(STDOUT_FILENO, "\x1b[H", 3);
83 perror(s);
84 exit(1);
85 }
86
87 void
88 disable_raw_mode()
89 {
90 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
91 die("tcsetattr in disable_raw_mode()");
92 }
93
94 void
95 enable_raw_mode()
96 {
97 struct termios raw;
98
99 if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
100 die("tcgetattr in enable_raw_mode()");
101 atexit(disable_raw_mode);
102
103 /* fix modifier keys, set 8 bits per char, ignore interrupts */
104 raw = E.orig_termios;
105 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
106 raw.c_oflag &= ~(OPOST);
107 raw.c_cflag |= (CS8);
108 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
109
110 /* set read() timeout in tenths of seconds */
111 raw.c_cc[VMIN] = 0;
112 raw.c_cc[VTIME] = 1;
113
114 /* apply terminal settings */
115 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
116 die("tcsetattr in enable_raw_mode()");
117 }
118
119 char
120 editor_read_key()
121 {
122 int nread;
123 char c;
124 while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
125 if (nread == -1 && errno != EAGAIN)
126 die("read in editor_read_key()");
127 }
128 return c;
129 }
130
131 /* get screen size by moving cursor and get current position */
132 int
133 get_cursor_position(int *rows, int *cols) {
134 char buf[32];
135 unsigned int i;
136
137 if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
138 return -1;
139
140 i = 0;
141 while (i < sizeof(buf) - 1) {
142 if (read(STDIN_FILENO, &buf[i], 1) != 1)
143 break;
144 if (buf[i] == 'R')
145 break;
146 ++i;
147 }
148 buf[i] = '\0';
149
150 if (buf[0] != '\x1b' || buf[1] != '[')
151 return -1;
152 if (sscanf(&buf[2], "%d;%d", rows, cols) != 2)
153 return -1;
154 return 0;
155 }
156
157 int
158 get_window_size(int *rows, int *cols)
159 {
160 struct winsize ws;
161
162 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == …
163 /* fallback screen size detection */
164 if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
165 return -1;
166 return get_cursor_position(rows, cols);
167 } else {
168 *cols = ws.ws_col;
169 *rows = ws.ws_row;
170 return 0;
171 }
172 }
173
174
175 /** SYNTAX HIGHLIGHTING **/
176
177 void
178 editor_update_syntax(eRow *row)
179 {
180 int i;
181 row->hl = realloc(row->hl, row->rsize);
182 memset(row->hl, ColorNormal, row->rsize);
183 for (i=0; i<row->rsize; ++i) {
184 if (isdigit(row->rchars[i]))
185 row->hl[i] = ColorDigit;
186 }
187 }
188
189
190 /** ROW OPERATIONS **/
191
192 /* navigate over tab-representative string as one character */
193 int
194 editor_row_cursor_x_to_rx(eRow *row, int cursor_x)
195 {
196 int rx, j;
197 rx = 0;
198 for (j=0; j<cursor_x; ++j) {
199 if (row->chars[j] == '\t')
200 rx += (tab_width - 1) - (rx % tab_width);
201 rx++;
202 }
203 return rx;
204 }
205
206 /* translate on-screen position to data position */
207 int
208 editor_row_cursor_rx_to_x(eRow *row, int cursor_rx)
209 {
210 int cur_rx, cx;
211 cur_rx = 0;
212 for (cx=0; cx<row->size; ++cx) {
213 if (row->chars[cx] == '\t')
214 cur_rx += (tab_width - 1) - (cur_rx % tab_width);
215 cur_rx++;
216
217 if (cur_rx > cursor_rx)
218 return cx;
219 }
220 return cx;
221 }
222
223 /* translate tabs before display */
224 void
225 editor_row_update(eRow* row)
226 {
227 int j, idx, tabs;
228
229 free(row->rchars);
230 row->rchars = malloc(row->size + 1);
231
232 tabs = 0;
233 for (j=0; j<row->size; ++j)
234 if (row->chars[j] == '\t')
235 tabs++;
236
237 free(row->rchars);
238 row->rchars = malloc(row->size + tabs*(tab_width - 1) + 1);
239
240 idx = 0;
241 for (j=0; j<row->size; ++j) {
242 if (row->chars[j] == '\t') {
243 row->rchars[idx++] = '>';
244 while (idx % tab_width != 0)
245 row->rchars[idx++] = ' ';
246 } else {
247 row->rchars[idx++] = row->chars[j];
248 }
249 }
250 row->rchars[idx] = '\0';
251 row->rsize = idx;
252 editor_update_syntax(row);
253 }
254
255 /* add row to buffer */
256 void
257 editor_row_insert(int i, char *s, size_t len)
258 {
259 if (i<0 || i>E.num_rows)
260 return;
261
262 E.row = realloc(E.row, sizeof(eRow) * (E.num_rows + 1));
263 memmove(&E.row[i+1], &E.row[i], sizeof(eRow) * (E.num_rows - i));
264
265 E.row[i].size = len;
266 E.row[i].chars = malloc(len + 1);
267 memcpy(E.row[i].chars, s, len);
268 E.row[i].chars[len] = '\0';
269
270 E.row[i].rsize = 0;
271 E.row[i].rchars = NULL;
272 E.row[i].hl = NULL;
273 editor_row_update(&E.row[i]);
274
275 ++E.num_rows;
276 E.file_changed = 1;
277 }
278
279 /* insert character to row, making sure to allocate memory accordingly */
280 void
281 editor_row_insert_char(eRow *row, int i, int c)
282 {
283 if (i<0 || i>row->size)
284 i = row->size;
285 row->chars = realloc(row->chars, row->size + 2);
286 memmove(&row->chars[i+1], &row->chars[i], row->size - i+1);
287 row->size++;
288 row->chars[i] = c;
289 editor_row_update(row);
290 E.file_changed = 1;
291 }
292
293 /* append a string to the end of a row */
294 void
295 editor_row_append_string(eRow *row, char *s, size_t len)
296 {
297 row->chars = realloc(row->chars, row->size + len + 1);
298 memcpy(&row->chars[row->size], s, len);
299 row->size += len;
300 row->chars[row->size] = '\0';
301 editor_row_update(row);
302 E.file_changed = 1;
303 }
304
305 void
306 editor_row_delete_char(eRow *row, int i)
307 {
308 if (i<0 || i>=row->size)
309 return;
310 memmove(&row->chars[i], &row->chars[i+1], row->size - i);
311 row->size--;
312 editor_row_update(row);
313 E.file_changed = 1;
314 }
315
316 void
317 editor_row_free(eRow *row)
318 {
319 free(row->chars);
320 free(row->rchars);
321 free(row->hl);
322 }
323
324 void
325 editor_row_delete(int i)
326 {
327 if (i<0 || i>=E.num_rows)
328 return;
329 editor_row_free(&E.row[i]);
330 memmove(&E.row[i], &E.row[i+1], sizeof(eRow)*(E.num_rows - i - 1…
331 E.num_rows--;
332 E.file_changed = 1;
333 }
334
335 void
336 editor_insert_char(int c)
337 {
338 if (E.cursor_y == E.num_rows)
339 editor_row_insert(E.num_rows, "", 0);
340 editor_row_insert_char(&E.row[E.cursor_y], E.cursor_x, c);
341 E.cursor_x++;
342 }
343
344 void
345 editor_insert_new_line()
346 {
347 eRow *row;
348 if (E.cursor_x == 0) {
349 editor_row_insert(E.cursor_y, "", 0);
350 } else {
351 row = &E.row[E.cursor_y];
352 editor_row_insert(E.cursor_y + 1, &row->chars[E.cursor_x…
353 row->size - E.cursor_x);
354 row = &E.row[E.cursor_y];
355 row->size = E.cursor_x;
356 row->chars[row->size] = '\0';
357 editor_row_update(row);
358 }
359 E.cursor_y++;
360 E.cursor_x = 0;
361 }
362
363 /* delete a character before the cursor, and allow for deletion across r…
364 void
365 editor_delete_char_left()
366 {
367 eRow *row;
368 if (E.cursor_y == E.num_rows || (E.cursor_x == 0 && E.cursor_y =…
369 return;
370
371 row = &E.row[E.cursor_y];
372 if (E.cursor_x > 0) {
373 editor_row_delete_char(row, E.cursor_x - 1);
374 E.cursor_x--;
375 } else {
376 E.cursor_x = E.row[E.cursor_y - 1].size;
377 editor_row_append_string(&E.row[E.cursor_y - 1],
378 row->chars, row->size);
379 editor_row_delete(E.cursor_y);
380 E.cursor_y--;
381 }
382 }
383
384 void
385 editor_delete_char_right()
386 {
387 if (E.cursor_y == E.num_rows)
388 return;
389 eRow *row = &E.row[E.cursor_y];
390 editor_row_delete_char(row, E.cursor_x);
391 }
392
393 /* convert rows to one long char array of length buflen */
394 char*
395 editor_concatenate_rows(int *buflen)
396 {
397 int totlen, j;
398 char *buf, *p;
399
400 totlen = 0;
401 for (j=0; j<E.num_rows; ++j)
402 totlen += E.row[j].size + 1; /* add space for newline c…
403 *buflen = totlen;
404
405 buf = malloc(totlen);
406 p = buf;
407 for (j=0; j<E.num_rows; ++j) {
408 memcpy(p, E.row[j].chars, E.row[j].size);
409 p += E.row[j].size;
410 *p = '\n';
411 p++;
412 }
413 return buf;
414 }
415
416
417 /** FILE IO **/
418
419 /* open file and read lines into memory */
420 void
421 file_open(char *filename)
422 {
423 free(E.filename);
424 E.filename = strdup(filename);
425
426 FILE *fp;
427 char *line;
428 size_t linecap;
429 ssize_t linelen;
430
431 fp = fopen(filename, "r");
432 if (!fp)
433 die("fopen in file_open");
434
435 line = NULL;
436 linecap = 0;
437 while ((linelen = getline(&line, &linecap, fp)) != -1) {
438 while (linelen > 0 && (line[linelen - 1] == '\n' ||
439 line[linelen - 1] == '\r'))
440 linelen--;
441 editor_row_insert(E.num_rows, line, linelen);
442 }
443 free(line);
444 fclose(fp);
445 E.file_changed = 0;
446 }
447
448 void
449 file_save(char *filename)
450 {
451 int len, fd;
452 char *buf;
453
454 if (filename == NULL) {
455 filename = editor_prompt("save as: %s");
456 if (filename == NULL) {
457 editor_set_status_message("save aborted");
458 return;
459 }
460 }
461
462 buf = editor_concatenate_rows(&len);
463
464 fd = open(filename, O_RDWR | O_CREAT, 0644);
465 if (fd != -1) {
466 if (ftruncate(fd, len) != -1) {
467 if (write(fd, buf, len) == len) {
468 close(fd);
469 free(buf);
470 E.filename = filename;
471 E.file_changed = 0;
472 editor_set_status_message("%d bytes writ…
473 return;
474 }
475 }
476 close(fd);
477 }
478 free(buf);
479 editor_set_status_message("error: can't save! I/O error %s",
480 strerror(errno));
481 }
482
483
484 /** FIND **/
485
486 /* reverse of strstr (3) */
487 char*
488 strrstr(char *haystack, char *needle,
489 size_t haystack_length, size_t needle_length)
490 {
491 char *cp;
492 for (cp = haystack + haystack_length - needle_length;
493 cp >= haystack;
494 cp--) {
495 if (strncmp(cp, needle, needle_length) == 0)
496 return cp;
497 }
498 return NULL;
499 }
500
501 /* find E.find_query from current cursor position moving in E.direction
502 * if opposite_direction = 0, and opposite E.direction if
503 * opposite_direction = 1 */
504 void
505 editor_find_next_occurence(int opposite_direction)
506 {
507 int y, y_inc, x_offset, query_len;
508 eRow *row;
509 char *match;
510
511 if (!E.find_query)
512 return;
513
514 if ((E.find_direction && !opposite_direction) ||
515 (!E.find_direction && opposite_direction))
516 y_inc = +1;
517 else
518 y_inc = -1;
519
520 x_offset = 0;
521 query_len = strlen(E.find_query);
522 /* from cursor until end of document */
523 for (y = E.cursor_y;
524 y != ((y_inc == +1) ? E.num_rows : -1);
525 y += y_inc) {
526 row = &E.row[y];
527
528 if (y == E.cursor_y)
529 x_offset = y_inc + E.cursor_x;
530 else
531 x_offset = 0;
532
533 if (y_inc == +1) {
534 match = strstr(row->chars + x_offset, E.find_que…
535 if (match)
536 break;
537 } else {
538 match = strrstr(row->chars, E.find_query,
539 (y == E.cursor_y) ? E.cursor_x :…
540 query_len);
541 if (match)
542 break;
543 }
544 }
545
546 if (match) {
547 E.cursor_y = y;
548 E.cursor_x = match - row->chars;
549 /* E.row_offset = E.num_rows; */ /* put line to top of s…
550
551 } else {
552 /* from other end of file until cursor */
553 for (y = (y_inc == +1) ? 0 : E.num_rows - 1;
554 y != E.cursor_y + y_inc;
555 y += y_inc) {
556 row = &E.row[y];
557 match = strstr(row->chars, E.find_query);
558 if (match)
559 break;
560 }
561 if (match) {
562 E.cursor_y = y;
563 E.cursor_x = editor_row_cursor_rx_to_x(row, matc…
564 }
565 }
566 }
567
568 /* direction = 1 is forward, 0 is backward */
569 void
570 editor_find(int direction)
571 {
572 char *query;
573
574 if (direction)
575 query = editor_prompt("/%s");
576 else
577 query = editor_prompt("?%s");
578 if (query == NULL)
579 return;
580
581 E.find_direction = direction;
582 free(E.find_query);
583 E.find_query = strdup(query);
584 editor_find_next_occurence(0);
585 free(query);
586 }
587
588
589 /** OUTPUT **/
590
591 void
592 editor_update_screen_size()
593 {
594 if (get_window_size(&E.screen_rows, &E.screen_columns) == -1)
595 die("get_window_size in main()");
596 E.screen_rows--;
597 if (E.show_status)
598 E.screen_rows--;
599 }
600
601 /* reallocate append buffer to hold string s */
602 void
603 ab_append(struct abuf *ab, const char *s, int len)
604 {
605 char *new;
606 new = realloc(ab->b, ab->len + len);
607
608 if (new == NULL)
609 return;
610 memcpy(&new[ab->len], s, len);
611 ab->b = new;
612 ab->len += len;
613 }
614
615 void
616 ab_free(struct abuf *ab) {
617 free(ab->b);
618 }
619
620 int
621 show_status_message()
622 {
623 if (E.show_status &&
624 strlen(E.status_msg) &&
625 time(NULL) - E.status_msg_time < status_message_timeout) {
626 return 1;
627 } else {
628 if (E.show_status) {
629 E.show_status = 0;
630 E.screen_rows++;
631 }
632 return 0;
633 }
634 }
635
636 /* draw status line consisting of left and right components.
637 * if the screen is narrower than both parts, just show the left status,
638 * truncated, if necessary. */
639 void
640 editor_draw_status(struct abuf *ab)
641 {
642 char left_status[512], right_status[512];
643 int left_status_len, right_status_len, padding;
644 int percentage;
645
646 left_status_len = 0;
647 switch (E.mode) {
648 case 1:
649 left_status_len = snprintf(left_status, sizeof(l…
650 "INSERT ");
651 break;
652 case 2:
653 left_status_len = snprintf(left_status, sizeof(l…
654 "VISUAL ");
655 break;
656 }
657 left_status_len += snprintf(left_status + left_status_len,
658 sizeof(left_status),
659 "%.20s %s",
660 E.filename ? E.filename : "[unnamed]…
661 E.file_changed ? "[+] " : "");
662
663 percentage = (int)((float)(E.cursor_y)/(E.num_rows-1)*100);
664 if (percentage < 0) percentage = 0;
665 right_status_len = snprintf(right_status, sizeof(right_status),
666 "%d%% < %d, %d",
667 percentage,
668 E.cursor_x+1, E.cursor_y+1);
669
670 if (left_status_len > E.screen_columns)
671 left_status_len = E.screen_columns;
672
673 padding = E.screen_columns - left_status_len - right_status_len;
674 if (padding < 0) {
675 if (left_status_len < E.screen_columns)
676 ab_append(ab, left_status, left_status_len);
677 else
678 ab_append(ab, left_status, E.screen_columns);
679 } else {
680 ab_append(ab, left_status, left_status_len);
681 while (padding--)
682 ab_append(ab, " ", 1);
683 ab_append(ab, right_status, right_status_len);
684 }
685
686 if (show_status_message()) {
687 ab_append(ab, "\x1b[m", 3);
688 ab_append(ab, "\r\n", 2);
689 }
690 }
691
692 /* draw status message if as long as it is specified and it is not too o…
693 void
694 editor_draw_status_message(struct abuf *ab)
695 {
696 int msglen;
697 ab_append(ab, "\x1b[K", 3);
698 msglen = strlen(E.status_msg);
699 if (msglen > E.screen_columns)
700 msglen = E.screen_columns;
701 if (show_status_message())
702 ab_append(ab, E.status_msg, msglen);
703 }
704
705 /* set vertical offset between file and screen when hitting the boundari…
706 void
707 editor_scroll()
708 {
709 E.cursor_rx = 0;
710 if (E.cursor_y < E.num_rows)
711 E.cursor_rx = editor_row_cursor_x_to_rx(&E.row[E.cursor_…
712 E.cursor_x);
713
714 if (E.cursor_y < E.row_offset)
715 E.row_offset = E.cursor_y;
716 else if (E.cursor_y >= E.row_offset + E.screen_rows)
717 E.row_offset = E.cursor_y - E.screen_rows + 1;
718
719 if (E.cursor_rx < E.column_offset)
720 E.column_offset = E.cursor_rx;
721 else if (E.cursor_rx >= E.column_offset + E.screen_columns)
722 E.column_offset = E.cursor_rx - E.screen_columns + 1;
723 }
724
725 /* draw editor screen.
726 * show tilde characters after EOF, and show status on last line */
727 void
728 editor_draw_rows(struct abuf *ab)
729 {
730 int y, j, len, file_row;
731 char *c;
732 unsigned char *hl;
733 char buf[16];
734 for (y = 0; y < E.screen_rows; ++y) {
735 file_row = y + E.row_offset;
736 if (file_row < E.num_rows) {
737 len = E.row[file_row].rsize - E.column_offset;
738 if (len < 0)
739 len = 0;
740 if (len > E.screen_columns)
741 len = E.screen_columns;
742 c = &E.row[file_row].rchars[E.column_offset];
743 hl = &E.row[file_row].hl[E.column_offset];
744 for (j = 0; j < len; ++j) {
745 if (hl[j] == ColorDigit) {
746 snprintf(buf, sizeof(buf),
747 "\x1b[3%dm", colors[Col…
748 ab_append(ab, buf, strlen(buf));
749 /* ab_append(ab, "\x1b[31m", 5);…
750 ab_append(ab, &c[j], 1);
751 ab_append(ab, "\x1b[39m", 5);
752 } else {
753 ab_append(ab, &c[j], 1);
754 }
755 }
756 } else {
757 ab_append(ab, "~", 1);
758 }
759
760 ab_append(ab, "\x1b[K", 3); /* erase to end of line */
761 ab_append(ab, "\r\n", 2);
762 }
763 }
764
765 /* fill output append buffer and write to terminal.
766 * move cursor to left before repaint, and to cursor position after.
767 * hide the cursor when repainting.
768 * VT100 escape sequences:
769 * - http://vt100.net/docs/vt100-ug/chapter3.html
770 * - http://vt100.net/docs/vt100-ug/chapter3.html#CUP */
771 void
772 editor_refresh_screen()
773 {
774 char buf[32];
775 struct abuf ab = ABUF_INIT;
776
777 show_status_message();
778 editor_scroll();
779
780 ab_append(&ab, "\x1b[?25l", 6); /* hide cursor */
781 ab_append(&ab, "\x1b[H", 3); /* cursor to home */
782
783 editor_draw_rows(&ab);
784 editor_draw_status(&ab);
785 editor_draw_status_message(&ab);
786
787 snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
788 (E.cursor_y - E.row_offset)+1,
789 (E.cursor_rx - E.column_offset)+1);
790 ab_append(&ab, buf, strlen(buf));
791
792 ab_append(&ab, "\x1b[?25h", 6); /* show cursor */
793
794 write(STDOUT_FILENO, ab.b, ab.len);
795 ab_free(&ab);
796 }
797
798 void
799 editor_place_cursor(int cx, int cy)
800 {
801 char buf[128];
802 snprintf(buf, sizeof(buf), "\x1b[%d;%dH", cy, cx);
803 write(STDOUT_FILENO, buf, strlen(buf));
804 }
805
806 /* set status message text, uses same format as printf */
807 void
808 editor_set_status_message(const char *fmt, ...)
809 {
810 va_list ap;
811 va_start(ap, fmt);
812 vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
813 va_end(ap);
814 E.status_msg_time = time(NULL);
815
816 if (!E.show_status) {
817 E.screen_rows--;
818 E.show_status = 1;
819 }
820 }
821
822
823 /** INPUT **/
824
825 /* prompt is expected to be a format string containing a %s */
826 char*
827 editor_prompt(char *prompt)
828 {
829 size_t bufsize, buflen;
830 char *buf;
831 int c;
832
833 bufsize = 128;
834 buflen = 0;
835 buf = malloc(bufsize);
836 buf[0] = '\0';
837
838 while (1) {
839 editor_update_screen_size();
840 editor_set_status_message(prompt, buf);
841 editor_refresh_screen();
842 editor_place_cursor(strlen(prompt) - 1 + strlen(buf),
843 E.screen_rows + 2);
844
845 c = editor_read_key();
846 if (c == CTRL_KEY('h') || c == 127) { /* detect backspac…
847 if (buflen != 0)
848 buf[--buflen] = '\0';
849 } else if (c == '\x1b' || (c == '\r' && !buflen)) { /* d…
850 editor_set_status_message("");
851 free(buf);
852 return NULL;
853 } else if (c == '\r') {
854 if (buflen != 0) {
855 editor_set_status_message("");
856 return buf;
857 }
858 } else if (!iscntrl(c) && c < 128) {
859 if (buflen >= bufsize - 1) {
860 bufsize *= 2;
861 buf = realloc(buf, bufsize);
862 }
863 buf[buflen++] = c;
864 buf[buflen] = '\0';
865 }
866 }
867 }
868
869 /* move cursor according to screen, file, and line limits */
870 void
871 editor_move_cursor(char key)
872 {
873 int row_len;
874 eRow *row;
875
876 switch(key) {
877 case 'h':
878 if (E.cursor_x != 0) {
879 E.cursor_x--;
880 } else if (E.cursor_y > 0) {
881 E.cursor_y--;
882 E.cursor_x = E.row[E.cursor_y].size;
883 }
884 break;
885 case 'j':
886 if (E.cursor_y < E.num_rows - 1)
887 E.cursor_y++;
888 break;
889 case 'k':
890 if (E.cursor_y != 0)
891 E.cursor_y--;
892 break;
893 case 'l':
894 row = (E.cursor_y >= E.num_rows) ? NULL : &E.row…
895 if (row && E.cursor_x < row->size - 1) {
896 E.cursor_x++;
897 } else if (row && E.cursor_x == row->size &&
898 E.cursor_y < E.num_rows - 1) {
899 E.cursor_y++;
900 E.cursor_x = 0;
901 }
902 break;
903 }
904
905 /* do not allow navigation past EOL by vertical navigation */
906 row = (E.cursor_y >= E.num_rows) ? NULL : &E.row[E.cursor_y];
907 row_len = row ? row->size : 0;
908 if (E.cursor_x > row_len)
909 E.cursor_x = row_len;
910 }
911
912 void
913 editor_read_command()
914 {
915 char* query;
916 query = editor_prompt(":%s");
917
918 editor_set_status_message("you typed: '%s'", query);
919 free(query);
920 }
921
922 /* first word in query is the command, the remaining are interpreted as …
923 void
924 editor_command(char *query)
925 {
926 switch(query) {
927
928 }
929 }
930
931
932 void
933 editor_process_keypress()
934 {
935 char c;
936 int i;
937
938 c = editor_read_key();
939
940 if (E.mode == 0) { /* normal mode */
941 switch (c) {
942 case 'i':
943 E.mode = 1;
944 break;
945 case 'a':
946 editor_move_cursor('l');
947 E.mode = 1;
948 break;
949
950 case 'h':
951 case 'j':
952 case 'k':
953 case 'l':
954 editor_move_cursor(c);
955 break;
956
957 case ':':
958 editor_read_command();
959 break;
960
961 case LEADER:
962 c = editor_read_key();
963 switch (c) {
964 case 'w':
965 file_save(E.filename);
966 break;
967 case 'q':
968 if (E.file_changed) {
969 editor_set_statu…
970 "error: …
971 "Press :…
972 break;
973 } else {
974 /* clear screen …
975 write(STDOUT_FIL…
976 write(STDOUT_FIL…
977 exit(0);
978 break;
979 }
980 }
981 break;
982
983 case CTRL_KEY('f'):
984 i = E.screen_rows;
985 while (i--)
986 editor_move_cursor('j');
987 break;
988 case CTRL_KEY('b'):
989 i = E.screen_rows;
990 while (i--)
991 editor_move_cursor('k');
992 break;
993
994 case CTRL_KEY('d'):
995 i = E.screen_rows/2;
996 while (i--)
997 editor_move_cursor('j');
998 break;
999 case CTRL_KEY('u'):
1000 i = E.screen_rows/2;
1001 while (i--)
1002 editor_move_cursor('k');
1003 break;
1004
1005 case '0':
1006 E.cursor_x = 0;
1007 break;
1008 case '$':
1009 if (E.cursor_y < E.num_rows)
1010 E.cursor_x = E.row[E.cursor_y].s…
1011 break;
1012
1013 case 'g':
1014 c = editor_read_key();
1015 if (c == 'g') {
1016 E.cursor_x = 0;
1017 E.cursor_y = 0;
1018 }
1019 break;
1020 case 'G':
1021 E.cursor_x = 0;
1022 E.cursor_y = E.num_rows - 1;
1023 break;
1024
1025 case 'x':
1026 editor_delete_char_right();
1027 break;
1028 case 'd':
1029 c = editor_read_key();
1030 if (c == 'd') {
1031 editor_row_delete(E.cursor_y);
1032 editor_move_cursor('h');
1033 }
1034 break;
1035
1036 case 'o':
1037 if (E.cursor_y < E.num_rows)
1038 E.cursor_x = E.row[E.cursor_y].s…
1039 editor_insert_new_line();
1040 E.mode = 1;
1041 break;
1042 case 'O':
1043 E.cursor_x = 0;
1044 editor_insert_new_line();
1045 editor_move_cursor('k');
1046 E.mode = 1;
1047 break;
1048
1049 case 'I':
1050 E.cursor_x = 0;
1051 E.mode = 1;
1052 break;
1053 case 'A':
1054 if (E.cursor_y < E.num_rows)
1055 E.cursor_x = E.row[E.cursor_y].s…
1056 E.mode = 1;
1057 break;
1058
1059 case '/':
1060 editor_find(1);
1061 break;
1062 case '?':
1063 editor_find(0);
1064 break;
1065 case 'n':
1066 editor_find_next_occurence(0);
1067 break;
1068 case 'N':
1069 editor_find_next_occurence(1);
1070 break;
1071 }
1072 } else if (E.mode == 1) { /* insert mode */
1073 switch (c) {
1074 case CTRL_KEY('c'):
1075 case '\x1b': /* escape */
1076 E.mode = 0;
1077 break;
1078
1079 case CTRL_KEY('\r'): /* enter */
1080 editor_insert_new_line();
1081 break;
1082
1083 case 127: /* backspace */
1084 case CTRL_KEY('h'):
1085 editor_delete_char_left();
1086 break;
1087
1088 case CTRL_KEY('l'):
1089 break;
1090
1091 default:
1092 editor_insert_char(c);
1093 break;
1094 }
1095 }
1096 }
1097
1098
1099 /** INIT **/
1100
1101 void
1102 deinit_editor() {
1103 int i;
1104 free(E.filename);
1105 free(E.find_query);
1106 for (i=0; i<E.num_rows; ++i)
1107 editor_row_free(&E.row[i]);
1108 free(E.row);
1109 }
1110
1111 /* set editor state variables, make room for status */
1112 void
1113 init_editor()
1114 {
1115 E.cursor_x = 0;
1116 E.cursor_y = 0;
1117 E.cursor_rx = 0;
1118 E.mode = 0;
1119 E.num_rows = 0;
1120 atexit(deinit_editor);
1121 E.row = NULL;
1122 E.row_offset = 0;
1123 E.column_offset = 0;
1124 E.filename = NULL;
1125 E.status_msg[0] = '\0';
1126 E.status_msg_time = 0;
1127 E.show_status = 0;
1128 E.file_changed = 0;
1129 E.find_query = NULL;
1130 }
1131
1132 void
1133 usage()
1134 {
1135 printf("%s: %s [OPTIONS] [FILES]\n"
1136 "\nOptional arguments:\n"
1137 " -h show this message\n"
1138 " -v show version information\n",
1139 __func__, PROGNAME);
1140 }
1141
1142 void
1143 version()
1144 {
1145 printf("%s version %s\n"
1146 "Licensed under the GNU Public License, v3+\n"
1147 "written by Anders Damsgaard, [email protected]\n"
1148 "https://gitlab.com/admesg/ve\n",
1149 PROGNAME, VERSION);
1150 }
1151
1152 /** MAIN **/
1153 int
1154 main(int argc, char *argv[])
1155 {
1156 ARGBEGIN {
1157 case 'h':
1158 usage();
1159 return 0;
1160 case 'v':
1161 version();
1162 return 0;
1163 default:
1164 usage();
1165 } ARGEND;
1166
1167 enable_raw_mode();
1168 init_editor();
1169
1170 editor_set_status_message("%s %s", PROGNAME, VERSION);
1171
1172 if (argv[0])
1173 file_open(argv[0]);
1174
1175 while (1) {
1176 editor_update_screen_size();
1177 editor_refresh_screen();
1178 editor_process_keypress();
1179 }
1180 return 0;
1181 }
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.