1 #include <stdio.h>
2 #include <string.h>
3 #include <termios.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include "eisl.h"
7 #include "fmt.h"
8 #include "except.h"
9 #include "str.h"
10 #include "mem.h"
11
12 #define TOKEN_MAX 80
13 #define FRAGMENT_MAX 80
14
15 #ifndef CTRL
16 #define CTRL(X) ((X) & 0x1F)
17 #endif
18
19 bool read_line_loop(int c, int *j, int *pos, int limit,
20 int *rl_line);
21
22 int
f_edit(int arglist)23 f_edit(int arglist)
24 {
25 int arg1,
26 res;
27
28 arg1 = car(arglist);
29 if (length(arglist) != 1)
30 error(WRONG_ARGS, "edit", arglist);
31 char *ed;
32 if ((ed = getenv("VISUAL")) == NULL) {
33 if ((ed = getenv("EDITOR")) == NULL) {
34 ed = "./edlis";
35 }
36 }
37 char *str =
38 Str_catv(ed, 1, 0, " ", 1, 0, GET_NAME(arg1), 1, 0, NULL);
39 res = system(str);
40 FREE(str);
41 if (res == -1)
42 error(SYSTEM_ERR, "edit", arg1);
43 f_load(arglist);
44 return (T);
45 }
46
47 void
setcolor(short n)48 setcolor(short n)
49 {
50 putp(tparm(set_a_foreground, n));
51 }
52
53 int
eisl_getch()54 eisl_getch()
55 {
56 struct termios oldt,
57 newt;
58 int ch;
59 tcgetattr(STDIN_FILENO, &oldt);
60 newt = oldt;
61 newt.c_lflag &= ~(ICANON | ECHO);
62 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
63 ch = getchar();
64 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
65 return ch;
66 }
67
68 // ------------REPL read-line-----------------
69 void
display_buffer()70 display_buffer()
71 {
72 int col;
73
74 ESCMVLEFT(3);
75 ESCCLSL();
76 col = 0;
77
78 while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
79
80 if (ed_incomment != -1 && line >= ed_incomment) { // comment
81 //
82 //
83 //
84 //
85 //
86 //
87 //
88 //
89 //
90 //
91 // #|...|#
92 ESCBOLD();
93 setcolor(ed_comment_color);
94 while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
95 putchar(buffer[col][0]);
96 col++;
97 if (buffer[col - 2][0] == '|' && buffer[col - 1][0] == '#') {
98 ed_incomment = -1;
99 ESCRST();
100 ESCFORG();
101 break;
102 }
103 }
104 ESCRST();
105 ESCFORG();
106 } else if (buffer[col][0] == ' ' ||
107 buffer[col][0] == '(' || buffer[col][0] == ')') {
108 putchar(buffer[col][0]);
109 col++;
110 } else {
111 switch (check_token_buffer(col)) {
112 case HIGHLIGHT_SYNTAX:
113 ESCBOLD();
114 setcolor(ed_syntax_color);
115 while (buffer[col][0] != ' ' &&
116 buffer[col][0] != '(' &&
117 buffer[col][0] != ')' &&
118 buffer[col][0] != NUL && buffer[col][0] != EOL) {
119 putchar(buffer[col][0]);
120 col++;
121 }
122 ESCRST();
123 ESCFORG();
124 break;
125 case HIGHLIGHT_BUILTIN:
126 ESCBOLD();
127 setcolor(ed_builtin_color);
128 while (buffer[col][0] != ' ' &&
129 buffer[col][0] != '(' &&
130 buffer[col][0] != ')' &&
131 buffer[col][0] != NUL && buffer[col][0] != EOL) {
132 putchar(buffer[col][0]);
133 col++;
134 }
135 ESCRST();
136 ESCFORG();
137 break;
138 case HIGHLIGHT_STRING:
139 ESCBOLD();
140 setcolor(ed_string_color);
141 putchar(buffer[col][0]);
142 col++;
143 while (buffer[col][0] != NUL && buffer[col][0] != EOL) {
144 putchar(buffer[col][0]);
145 col++;
146 if (buffer[col - 1][0] == '"')
147 break;
148 }
149 ESCRST();
150 ESCFORG();
151 break;
152 case HIGHLIGHT_COMMENT:
153 ESCBOLD();
154 setcolor(ed_comment_color);
155 while (buffer[col][0] != NUL && buffer[col][0] != EOL) {
156 putchar(buffer[col][0]);
157 col++;
158 }
159 ESCRST();
160 ESCFORG();
161 break;
162 case HIGHLIGHT_EXTENDED:
163 ESCBOLD();
164 setcolor(ed_extended_color);
165 while (buffer[col][0] != ' ' &&
166 buffer[col][0] != '(' &&
167 buffer[col][0] != ')' &&
168 buffer[col][0] != NUL && buffer[col][0] != EOL) {
169 putchar(buffer[col][0]);
170 col++;
171 }
172 ESCRST();
173 ESCFORG();
174 break;
175 case HIGHLIGHT_MULTILINE_COMMENT:
176 ESCBOLD();
177 setcolor(ed_comment_color);
178 ed_incomment = line;
179 while (buffer[col][0] != EOL && buffer[col][0] != NUL) {
180 putchar(buffer[col][0]);
181 col++;
182 if (buffer[col - 2][0] == '|'
183 && buffer[col - 1][0] == '#') {
184 ed_incomment = -1;
185 ESCRST();
186 ESCFORG();
187 break;
188 }
189 }
190 break;
191 default:
192 while (buffer[col][0] != ' ' &&
193 buffer[col][0] != '(' &&
194 buffer[col][0] != ')' &&
195 buffer[col][0] != NUL && buffer[col][0] != EOL) {
196 putchar(buffer[col][0]);
197 col++;
198 }
199 }
200 }
201 }
202 ESCRST();
203 return;
204 }
205
206
207 enum HighlightToken
check_token_buffer(int col)208 check_token_buffer(int col)
209 {
210 char str[TOKEN_MAX];
211 int pos;
212
213 pos = 0;
214 if (buffer[col][0] == '"')
215 return HIGHLIGHT_STRING;
216 else if (buffer[col][0] == ';')
217 return HIGHLIGHT_COMMENT;
218 while (buffer[col][0] != ' ' &&
219 buffer[col][0] != '(' &&
220 buffer[col][0] != ')' &&
221 buffer[col][0] != NUL && buffer[col][0] != EOL) {
222 str[pos] = buffer[col][0];
223 col++;
224 pos++;
225 }
226 str[pos] = NUL;
227 if (str[0] == '#' && str[1] == '|')
228 return HIGHLIGHT_MULTILINE_COMMENT; // #|...|#
229 if (pos == 0)
230 return HIGHLIGHT_NONE;
231 return maybe_match(str);
232 }
233
234
235 int
findlparen_buffer(int col)236 findlparen_buffer(int col)
237 {
238 int nest;
239
240 col--;
241 nest = 0;
242 while (col >= 0) {
243 if (buffer[col][0] == '(' && nest == 0)
244 break;
245 else if (buffer[col][0] == ')')
246 nest++;
247 else if (buffer[col][0] == '(')
248 nest--;
249
250 col--;
251 }
252 return (col);
253 }
254
255 int
findrparen_buffer(int col)256 findrparen_buffer(int col)
257 {
258 int nest,
259 limit;
260
261 col++;
262 nest = 0;
263 for (limit = 0; limit <= COL_SIZE; limit++)
264 if (buffer[limit][0] == 0)
265 break;
266
267 while (col <= limit) {
268 if (buffer[col][0] == ')' && nest == 0)
269 break;
270 else if (buffer[col][0] == '(')
271 nest++;
272 else if (buffer[col][0] == ')')
273 nest--;
274
275 col++;
276 }
277 if (col > limit)
278 return (-1);
279 else
280 return (col);
281 }
282
283 void
emphasis_rparen_buffer(int col)284 emphasis_rparen_buffer(int col)
285 {
286 int pos;
287
288 if (buffer[col][0] != '(')
289 return;
290 pos = findrparen_buffer(col);
291 if (pos < 0)
292 return;
293
294 ESCMVLEFT(col + 3);
295 ESCBCYAN();
296 putchar('(');
297 ESCBORG();
298 ESCMVLEFT(pos + 3);
299 ESCBCYAN();
300 putchar(')');
301 ESCBORG();
302 ed_rparen_col = pos;
303 ed_lparen_col = col;
304 ESCMVLEFT(col + 3);
305 }
306
307
308 void
emphasis_lparen_buffer(int col)309 emphasis_lparen_buffer(int col)
310 {
311 int pos;
312
313 if (buffer[col][0] != ')')
314 return;
315
316 pos = findlparen_buffer(col);
317 if (pos < 0)
318 return;
319
320 ESCMVLEFT(col + 3);
321 ESCBCYAN();
322 putchar(')');
323 ESCBORG();
324 ESCMVLEFT(pos + 3);
325 ESCBCYAN();
326 putchar('(');
327 ESCBORG();
328 ed_rparen_col = col;
329 ed_lparen_col = pos;
330 ESCMVLEFT(col + 3);
331 }
332
333
334 void
reset_paren_buffer()335 reset_paren_buffer()
336 {
337 ed_lparen_col = -1;
338 ed_rparen_col = -1;
339 }
340
341 void
restore_paren_buffer(int col)342 restore_paren_buffer(int col)
343 {
344
345 if (ed_lparen_col != -1) {
346 ESCMVLEFT(ed_lparen_col + 3);
347 ESCBORG();
348 putchar('(');
349 ed_lparen_col = -1;
350 }
351 if (ed_rparen_col != -1) {
352 ESCMVLEFT(ed_rparen_col + 3);
353 ESCBORG();
354 putchar(')');
355 ed_rparen_col = -1;
356 }
357 ESCMVLEFT(col + 3);
358 }
359
360
361 char *
get_fragment_buffer(int col)362 get_fragment_buffer(int col)
363 {
364 static char str[FRAGMENT_MAX];
365 int pos;
366
367 while (col >= 0 &&
368 buffer[col][0] != ' ' &&
369 buffer[col][0] != '(' && buffer[col][0] != ')') {
370 col--;
371 }
372 col++;
373 pos = 0;
374 while (buffer[col][0] != ' ' &&
375 buffer[col][0] != '(' && buffer[col][0] >= ' ') {
376 str[pos] = buffer[col][0];
377 col++;
378 pos++;
379 }
380 str[pos] = NUL;
381 return (str);
382 }
383
384
385 void
find_candidate_buffer(int col)386 find_candidate_buffer(int col)
387 {
388 char *str;
389
390 str = get_fragment_buffer(col);
391 ed_candidate_pt = 0;
392 if (str[0] != NUL)
393 gather_fuzzy_matches(str, ed_candidate, &ed_candidate_pt);
394 }
395
396 int
replace_fragment_buffer(const char * newstr,int col)397 replace_fragment_buffer(const char *newstr, int col)
398 {
399 char *oldstr;
400 int m,
401 n;
402
403 oldstr = get_fragment_buffer(col);
404 m = strlen(oldstr);
405 n = strlen(newstr);
406 col--;
407 while (m > 0) {
408 backspace_buffer(col);
409 m--;
410 col--;
411 }
412 col++;
413 while (n > 0) {
414 insertcol_buffer(col);
415 buffer[col][0] = *newstr;
416 col++;
417 newstr++;
418 n--;
419 }
420
421 return (col);
422 }
423
424 void
backspace_buffer(int col)425 backspace_buffer(int col)
426 {
427 int i;
428
429 for (i = col; i < COL_SIZE; i++)
430 buffer[i][0] = buffer[i + 1][0];
431 buffer[COL_SIZE][0] = 0;
432 }
433
434 void
insertcol_buffer(int col)435 insertcol_buffer(int col)
436 {
437 int i;
438
439 for (i = COL_SIZE; i > col; i--)
440 buffer[i][0] = buffer[i - 1][0];
441 }
442
443 static void
right(int * j)444 right(int *j)
445 {
446 if (buffer[*j][0] == 0)
447 return;
448 (*j)++;
449 restore_paren_buffer(*j);
450 emphasis_lparen_buffer(*j);
451 emphasis_rparen_buffer(*j);
452 ESCMVLEFT(*j + 3);
453 }
454
455 static void
left(int * j)456 left(int *j)
457 {
458 if (*j <= 0)
459 return;
460 (*j)--;
461 restore_paren_buffer(*j);
462 emphasis_lparen_buffer(*j);
463 emphasis_rparen_buffer(*j);
464 ESCMVLEFT(*j + 3);
465 }
466
467 int
read_line(int flag)468 read_line(int flag)
469 {
470 int j,
471 rl_line;
472 static int pos = 0;
473
474 if (flag == -1) {
475 pos--;
476 return (-1);
477 }
478
479 if (buffer[pos][0] == 0) {
480 int c,
481 i;
482 static int limit = 0;
483
484 for (i = 9; i > 0; i--)
485 for (j = 0; j <= COL_SIZE; j++)
486 buffer[j][i] = buffer[j][i - 1];
487
488 limit++;
489 if (limit >= 10)
490 limit = 9;
491
492 for (j = 0; j <= COL_SIZE; j++)
493 buffer[j][0] = 0;
494
495 rl_line = 0;
496 ed_lparen_col = -1;
497 ed_rparen_col = -1;
498 j = 0;
499 c = eisl_getch();
500 while (!read_line_loop(c, &j, &pos, limit, &rl_line)) {
501 c = eisl_getch();
502 }
503 }
504 return (buffer[pos++][0]);
505 }
506
507 void
up(int limit,int * rl_line,int * j,int * pos)508 up(int limit, int *rl_line, int *j, int *pos)
509 {
510 if (limit <= 1)
511 return;
512 if (*rl_line >= limit - 1)
513 *rl_line = limit - 2;
514 for (*j = 0; *j <= COL_SIZE; (*j)++)
515 buffer[*j][0] = buffer[*j][*rl_line + 1];
516
517 for (*j = 0; *j <= COL_SIZE; (*j)++)
518 if (buffer[*j][0] == EOL)
519 break;
520 (*rl_line)++;
521 *pos = 0;
522 ed_rparen_col = -1;
523 ed_lparen_col = -1;
524 display_buffer();
525 }
526
527 void
down(int * rl_line,int * j,int * pos)528 down(int *rl_line, int *j, int *pos)
529 {
530 if (*rl_line <= 1)
531 *rl_line = 1;
532 for (*j = 0; *j <= COL_SIZE; (*j)++)
533 buffer[*j][0] = buffer[*j][*rl_line - 1];
534 for (*j = 0; *j <= COL_SIZE; (*j)++)
535 if (buffer[*j][0] == EOL)
536 break;
537 (*rl_line)--;
538 *pos = 0;
539 ed_rparen_col = -1;
540 ed_lparen_col = -1;
541 display_buffer();
542 }
543
544 bool
read_line_loop(int c,int * j,int * pos,int limit,int * rl_line)545 read_line_loop(int c, int *j, int *pos, int limit, int *rl_line)
546 {
547 int k;
548
549 switch (c) {
550 case CTRL('M'):
551 case EOL:
552 for (*j = 0; *j <= COL_SIZE; (*j)++)
553 if (buffer[*j][0] == 0) {
554 buffer[*j][0] = c;
555 break;
556 }
557 restore_paren_buffer(*j);
558 putchar(c);
559 *pos = 0;
560 return true;
561 case CTRL('H'):
562 case DEL:
563 if (*j <= 0)
564 break;
565 (*j)--;
566 for (k = *j; k < COL_SIZE; k++)
567 buffer[k][0] = buffer[k + 1][0];
568 display_buffer();
569 ESCMVLEFT(*j + 3);
570 if (ed_rparen_col > *j)
571 ed_rparen_col--;
572 if (ed_lparen_col > *j)
573 ed_lparen_col--;
574 break;
575 case CTRL('D'):
576 for (k = *j; k < COL_SIZE; k++)
577 buffer[k][0] = buffer[k + 1][0];
578 display_buffer();
579 ESCMVLEFT(*j + 3);
580 if (ed_rparen_col > *j)
581 ed_rparen_col--;
582 if (ed_lparen_col > *j)
583 ed_lparen_col--;
584 break;
585 case CTRL('K'):
586 buffer1[0] = '\0';
587 for (k = *j; k < COL_SIZE; k++) {
588 buffer1[k - *j] = buffer[k][0];
589 buffer[k][0] = NUL;
590 }
591 display_buffer();
592 ESCMVLEFT(*j + 3);
593 break;
594 case CTRL('Y'):
595 for (k = 0; k < COL_SIZE; k++)
596 buffer[k][0] = buffer1[k];
597
598 for (k = 0; k < COL_SIZE; k++) {
599 if (buffer[k][0] == NUL)
600 break;
601 }
602
603 display_buffer();
604 *j = k;
605 ESCMVLEFT(k + 3);
606 break;
607 case CTRL('A'):
608 *j = 0;
609 ESCMVLEFT(*j + 3);
610 break;
611 case CTRL('E'):
612 for (k = 0; k < COL_SIZE; k++) {
613 if (buffer[k][0] == NUL)
614 break;
615 }
616
617 display_buffer();
618 *j = k;
619 ESCMVLEFT(k + 3);
620 break;
621 case CTRL('F'):
622 right(j);
623 break;
624 case CTRL('B'):
625 left(j);
626 break;
627 case CTRL('P'):
628 up(limit, rl_line, j, pos);
629 break;
630 case CTRL('N'):
631 down(rl_line, j, pos);
632 break;
633 case ESC:
634 c = eisl_getch();
635 switch (c) {
636 case TAB:
637 find_candidate_buffer(*j); // completion
638 if (ed_candidate_pt == 0)
639 break;
640 else if (ed_candidate_pt == 1) {
641 *j = replace_fragment_buffer(ed_candidate[0], *j);
642 display_buffer();
643 ESCMVLEFT(*j + 3);
644 } else {
645 const int CANDIDATE = 3;
646 int i;
647
648 k = 0;
649 ESCSCR();
650 ESCMVLEFT(1);
651 bool more_candidates_selected;
652 do {
653 more_candidates_selected = false;
654 ESCREV();
655 for (i = 0; i < CANDIDATE; i++) {
656 if (i + k >= ed_candidate_pt)
657 break;
658 Fmt_print("%d:%s ", i + 1, ed_candidate[i + k]);
659 }
660 if (ed_candidate_pt > k + CANDIDATE)
661 Fmt_print("4:more");
662 ESCRST();
663 bool bad_candidate_selected;
664 do {
665 bad_candidate_selected = false;
666 c = eisl_getch();
667 if (c != ESC) {
668 i = c - '1';
669 more_candidates_selected =
670 ed_candidate_pt > k + CANDIDATE
671 && i == CANDIDATE;
672 if (more_candidates_selected) {
673 k = k + CANDIDATE;
674 ESCMVLEFT(1);
675 ESCCLSL();
676 break;
677 }
678 bad_candidate_selected =
679 i + k >= ed_candidate_pt || i < 0
680 || c == EOL;
681 }
682 }
683 while (bad_candidate_selected);
684 }
685 while (more_candidates_selected);
686 if (c != ESC)
687 *j = replace_fragment_buffer(ed_candidate[i + k], *j);
688 ESCMVLEFT(1);
689 ESCCLSL();
690 ESCMVU();
691 ESCMVLEFT(3);
692 display_buffer();
693 ESCMVLEFT(*j + 3);
694 }
695 return false;
696 case 'q': // Esc+q
697 putchar('\n');
698 greeting_flag = false;
699 RAISE(Exit_Interp);
700 break;
701 case ARROW_PREFIX:
702 c = eisl_getch();
703 if (c == ed_key_up) {
704 up(limit, rl_line, j, pos);
705 } else if (c == ed_key_down) {
706 down(rl_line, j, pos);
707 } else if (c == ed_key_left) {
708 left(j);
709 } else if (c == ed_key_right) {
710 right(j);
711 }
712 }
713 break;
714
715 default:
716 for (k = COL_SIZE; k > *j; k--)
717 buffer[k][0] = buffer[k - 1][0];
718 buffer[(*j)++][0] = c;
719 display_buffer();
720 reset_paren_buffer();
721 if (c == '(' || c == ')') {
722 emphasis_lparen_buffer(*j - 1);
723 emphasis_rparen_buffer(*j - 1);
724 } else {
725 if (ed_rparen_col >= *j - 1)
726 ed_rparen_col++;
727 if (ed_lparen_col >= *j - 1)
728 ed_lparen_col++;
729 }
730 ESCMVLEFT(*j + 3);
731
732 }
733 return false;
734 }
735