tvi.c - neatvi - [fork] simple vi-type editor with UTF-8 support
(HTM) git clone git://src.adamsgaard.dk/neatvi
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
tvi.c (29670B)
---
1 /*
2 * NEATVI Editor
3 *
4 * Copyright (C) 2015-2019 Ali Gholami Rudi <ali at rudi dot ir>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <signal.h>
24 #include "vi.h"
25
26 static char vi_msg[EXLEN]; /* current message */
27 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
28 static int vi_charcmd; /* the character finding command */
29 static int vi_arg1, vi_arg2; /* the first and second arguments */
30 static int vi_ybuf; /* current yank buffer */
31 static int vi_pcol; /* the column requested by | command */
32 static int vi_printed; /* ex_print() calls since the last command */
33 static int vi_scroll; /* scroll amount for ^f and ^d*/
34 static int vi_soset, vi_so; /* search offset; 1 in "/kw/1" */
35
36 static void vi_wait(void)
37 {
38 if (vi_printed > 1) {
39 free(ex_read("[enter to continue]"));
40 vi_msg[0] = '\0';
41 }
42 vi_printed = 0;
43 }
44
45 static void vi_drawmsg(void)
46 {
47 int oleft = xleft;
48 xleft = 0;
49 led_printmsg(vi_msg, xrows, "---");
50 vi_msg[0] = '\0';
51 xleft = oleft;
52 }
53
54 static void vi_drawrow(int row)
55 {
56 char *s = lbuf_get(xb, row);
57 if (xhll && row == xrow)
58 syn_context(conf_hlline());
59 led_print(s ? s : (row ? "~" : ""), row - xtop, ex_filetype());
60 syn_context(0);
61 }
62
63 /* redraw the screen */
64 static void vi_drawagain(int xcol, int lineonly)
65 {
66 int i;
67 term_record();
68 for (i = xtop; i < xtop + xrows; i++)
69 if (!lineonly || i == xrow)
70 vi_drawrow(i);
71 vi_drawmsg();
72 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
73 term_commit();
74 }
75
76 /* update the screen */
77 static void vi_drawupdate(int xcol, int otop)
78 {
79 int i = 0;
80 if (otop != xtop) {
81 term_record();
82 term_pos(0, 0);
83 term_room(otop - xtop);
84 if (xtop > otop) {
85 int n = MIN(xtop - otop, xrows);
86 for (i = 0; i < n; i++)
87 vi_drawrow(xtop + xrows - n + i);
88 } else {
89 int n = MIN(otop - xtop, xrows);
90 for (i = 0; i < n; i++)
91 vi_drawrow(xtop + i);
92 }
93 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
94 term_commit();
95 }
96 vi_drawmsg();
97 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
98 }
99
100 /* update the screen by removing lines r1 to r2 before an input command */
101 static void vi_drawrm(int r1, int r2, int newln)
102 {
103 r1 = MIN(MAX(r1, xtop), xtop + xrows);
104 r2 = MIN(MAX(r2, xtop), xtop + xrows);
105 term_pos(r1 - xtop, 0);
106 term_room(r1 - r2 + newln);
107 }
108
109 static int vi_buf[128];
110 static int vi_buflen;
111
112 static int vi_read(void)
113 {
114 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
115 }
116
117 static void vi_back(int c)
118 {
119 if (vi_buflen < sizeof(vi_buf))
120 vi_buf[vi_buflen++] = c;
121 }
122
123 static char *vi_char(void)
124 {
125 return led_read(&xkmap);
126 }
127
128 static char *vi_prompt(char *msg, int *kmap)
129 {
130 char *r, *s;
131 term_pos(xrows, led_pos(msg, 0));
132 term_kill();
133 s = led_prompt(msg, "", kmap, "---");
134 if (!s)
135 return NULL;
136 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
137 free(s);
138 return r;
139 }
140
141 /* read an ex input line */
142 char *ex_read(char *msg)
143 {
144 struct sbuf *sb;
145 int c;
146 if (xled) {
147 int oleft = xleft;
148 char *s = led_prompt(msg, "", &xkmap, "---");
149 xleft = oleft;
150 if (s)
151 term_chr('\n');
152 return s;
153 }
154 sb = sbuf_make();
155 while ((c = getchar()) != EOF && c != '\n')
156 sbuf_chr(sb, c);
157 if (c == EOF) {
158 sbuf_free(sb);
159 return NULL;
160 }
161 return sbuf_done(sb);
162 }
163
164 /* show an ex message */
165 void ex_show(char *msg)
166 {
167 if (xvis) {
168 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
169 } else if (xled) {
170 led_print(msg, -1, "---");
171 term_chr('\n');
172 } else {
173 printf("%s", msg);
174 }
175 }
176
177 /* print an ex output line */
178 void ex_print(char *line)
179 {
180 if (xvis) {
181 vi_printed += line ? 1 : 2;
182 if (line)
183 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
184 if (line)
185 led_print(line, -1, "");
186 term_chr('\n');
187 } else {
188 if (line)
189 ex_show(line);
190 }
191 }
192
193 static int vi_yankbuf(void)
194 {
195 int c = vi_read();
196 if (c == '"')
197 return vi_read();
198 vi_back(c);
199 return 0;
200 }
201
202 static int vi_prefix(void)
203 {
204 int n = 0;
205 int c = vi_read();
206 if ((c >= '1' && c <= '9')) {
207 while (isdigit(c)) {
208 n = n * 10 + c - '0';
209 c = vi_read();
210 }
211 }
212 vi_back(c);
213 return n;
214 }
215
216 static int vi_col2off(struct lbuf *lb, int row, int col)
217 {
218 char *ln = lbuf_get(lb, row);
219 return ln ? ren_off(ln, col) : 0;
220 }
221
222 static int vi_off2col(struct lbuf *lb, int row, int off)
223 {
224 char *ln = lbuf_get(lb, row);
225 return ln ? ren_pos(ln, off) : 0;
226 }
227
228 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
229 {
230 int o = *off + dir;
231 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
232 return 1;
233 *off = o;
234 return 0;
235 }
236
237 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
238 {
239 char *ln = lbuf_get(lb, *row);
240 int col = ln ? ren_pos(ln, *off) : 0;
241 int o = ln ? ren_next(ln, col, dir) : -1;
242 if (o < 0)
243 return -1;
244 *off = ren_off(ln, o);
245 return 0;
246 }
247
248 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
249 {
250 if (cs != vi_charlast)
251 strcpy(vi_charlast, cs);
252 vi_charcmd = cmd;
253 return lbuf_findchar(lb, cs, cmd, n, row, off);
254 }
255
256 static int vi_search(int cmd, int cnt, int *row, int *off)
257 {
258 char *kwd;
259 int r = *row;
260 int o = *off;
261 char *failed = NULL;
262 int len = 0;
263 int i, dir;
264 if (cmd == '/' || cmd == '?') {
265 char sign[4] = {cmd};
266 struct sbuf *sb;
267 char *kw = vi_prompt(sign, &xkmap);
268 char *re;
269 if (!kw)
270 return 1;
271 sb = sbuf_make();
272 sbuf_chr(sb, cmd);
273 sbuf_str(sb, kw);
274 free(kw);
275 kw = sbuf_buf(sb);
276 if ((re = re_read(&kw))) {
277 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
278 while (isspace(*kw))
279 kw++;
280 vi_soset = !!kw[0];
281 vi_so = atoi(kw);
282 free(re);
283 }
284 sbuf_free(sb);
285 }
286 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
287 return 1;
288 dir = cmd == 'N' ? -dir : dir;
289 o = *off;
290 for (i = 0; i < cnt; i++) {
291 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
292 failed = " not found";
293 break;
294 }
295 if (i + 1 < cnt && cmd == '/')
296 o += len;
297 }
298 if (!failed) {
299 *row = r;
300 *off = o;
301 if (vi_soset) {
302 *off = -1;
303 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
304 failed = " bad offset";
305 else
306 *row += vi_so;
307 }
308 }
309 if (failed != NULL)
310 snprintf(vi_msg, sizeof(vi_msg), "/%s/%s\n", kwd, failed ? failed : "");
311 return failed != NULL;
312 }
313
314 /* read a line motion */
315 static int vi_motionln(int *row, int cmd)
316 {
317 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
318 int c = vi_read();
319 int mark, mark_row, mark_off;
320 switch (c) {
321 case '\n':
322 case '+':
323 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
324 break;
325 case '-':
326 *row = MAX(*row - cnt, 0);
327 break;
328 case '_':
329 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
330 break;
331 case '\'':
332 if ((mark = vi_read()) <= 0)
333 return -1;
334 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
335 return -1;
336 *row = mark_row;
337 break;
338 case 'j':
339 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
340 break;
341 case 'k':
342 *row = MAX(*row - cnt, 0);
343 break;
344 case 'G':
345 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
346 break;
347 case 'H':
348 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
349 break;
350 case 'L':
351 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
352 break;
353 case 'M':
354 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
355 break;
356 default:
357 if (c == cmd) {
358 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
359 break;
360 }
361 if (c == '%' && (vi_arg1 || vi_arg2)) {
362 if (cnt > 100)
363 return -1;
364 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
365 break;
366 }
367 vi_back(c);
368 return 0;
369 }
370 if (*row < 0)
371 *row = 0;
372 return c;
373 }
374
375 static char *vi_curword(struct lbuf *lb, int row, int off)
376 {
377 struct sbuf *sb;
378 char *ln = lbuf_get(lb, row);
379 char *beg, *end;
380 if (!ln)
381 return NULL;
382 beg = uc_chr(ln, ren_noeol(ln, off));
383 end = beg;
384 while (*end && uc_kind(end) == 1)
385 end = uc_next(end);
386 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
387 beg = uc_beg(ln, beg - 1);
388 if (beg >= end)
389 return NULL;
390 sb = sbuf_make();
391 sbuf_str(sb, "\\<");
392 sbuf_mem(sb, beg, end - beg);
393 sbuf_str(sb, "\\>");
394 return sbuf_done(sb);
395 }
396
397 /* read a motion */
398 static int vi_motion(int *row, int *off)
399 {
400 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
401 char *ln = lbuf_get(xb, *row);
402 int dir = dir_context(ln ? ln : "");
403 int mark, mark_row, mark_off;
404 char *cs;
405 int mv;
406 int i;
407 if ((mv = vi_motionln(row, 0))) {
408 *off = -1;
409 return mv;
410 }
411 mv = vi_read();
412 switch (mv) {
413 case 'f':
414 if (!(cs = vi_char()))
415 return -1;
416 if (vi_findchar(xb, cs, mv, cnt, row, off))
417 return -1;
418 break;
419 case 'F':
420 if (!(cs = vi_char()))
421 return -1;
422 if (vi_findchar(xb, cs, mv, cnt, row, off))
423 return -1;
424 break;
425 case ';':
426 if (!vi_charlast[0])
427 return -1;
428 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
429 return -1;
430 break;
431 case ',':
432 if (!vi_charlast[0])
433 return -1;
434 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
435 return -1;
436 break;
437 case 'h':
438 for (i = 0; i < cnt; i++)
439 if (vi_nextcol(xb, -1 * dir, row, off))
440 break;
441 break;
442 case 'l':
443 for (i = 0; i < cnt; i++)
444 if (vi_nextcol(xb, +1 * dir, row, off))
445 break;
446 break;
447 case 't':
448 if (!(cs = vi_char()))
449 return -1;
450 if (vi_findchar(xb, cs, mv, cnt, row, off))
451 return -1;
452 break;
453 case 'T':
454 if (!(cs = vi_char()))
455 return -1;
456 if (vi_findchar(xb, cs, mv, cnt, row, off))
457 return -1;
458 break;
459 case 'B':
460 for (i = 0; i < cnt; i++)
461 if (lbuf_wordend(xb, 1, -1, row, off))
462 break;
463 break;
464 case 'E':
465 for (i = 0; i < cnt; i++)
466 if (lbuf_wordend(xb, 1, +1, row, off))
467 break;
468 break;
469 case 'W':
470 for (i = 0; i < cnt; i++)
471 if (lbuf_wordbeg(xb, 1, +1, row, off))
472 break;
473 break;
474 case 'b':
475 for (i = 0; i < cnt; i++)
476 if (lbuf_wordend(xb, 0, -1, row, off))
477 break;
478 break;
479 case 'e':
480 for (i = 0; i < cnt; i++)
481 if (lbuf_wordend(xb, 0, +1, row, off))
482 break;
483 break;
484 case 'w':
485 for (i = 0; i < cnt; i++)
486 if (lbuf_wordbeg(xb, 0, +1, row, off))
487 break;
488 break;
489 case '{':
490 for (i = 0; i < cnt; i++)
491 if (lbuf_paragraphbeg(xb, -1, row, off))
492 break;
493 break;
494 case '}':
495 for (i = 0; i < cnt; i++)
496 if (lbuf_paragraphbeg(xb, +1, row, off))
497 break;
498 break;
499 case '[':
500 if (vi_read() != '[')
501 return -1;
502 for (i = 0; i < cnt; i++)
503 if (lbuf_sectionbeg(xb, -1, row, off))
504 break;
505 break;
506 case ']':
507 if (vi_read() != ']')
508 return -1;
509 for (i = 0; i < cnt; i++)
510 if (lbuf_sectionbeg(xb, +1, row, off))
511 break;
512 break;
513 case '0':
514 *off = 0;
515 break;
516 case '^':
517 *off = lbuf_indents(xb, *row);
518 break;
519 case '$':
520 *off = lbuf_eol(xb, *row);
521 break;
522 case '|':
523 *off = vi_col2off(xb, *row, cnt - 1);
524 vi_pcol = cnt - 1;
525 break;
526 case '/':
527 if (vi_search(mv, cnt, row, off))
528 return -1;
529 break;
530 case '?':
531 if (vi_search(mv, cnt, row, off))
532 return -1;
533 break;
534 case 'n':
535 if (vi_search(mv, cnt, row, off))
536 return -1;
537 break;
538 case 'N':
539 if (vi_search(mv, cnt, row, off))
540 return -1;
541 break;
542 case TK_CTL('a'):
543 if (!(cs = vi_curword(xb, *row, *off)))
544 return -1;
545 ex_kwdset(cs, +1);
546 vi_soset = 0;
547 free(cs);
548 if (vi_search('n', cnt, row, off))
549 return -1;
550 break;
551 case ' ':
552 for (i = 0; i < cnt; i++)
553 if (vi_nextoff(xb, +1, row, off))
554 break;
555 break;
556 case 127:
557 case TK_CTL('h'):
558 for (i = 0; i < cnt; i++)
559 if (vi_nextoff(xb, -1, row, off))
560 break;
561 break;
562 case '`':
563 if ((mark = vi_read()) <= 0)
564 return -1;
565 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
566 return -1;
567 *row = mark_row;
568 *off = mark_off;
569 break;
570 case '%':
571 if (lbuf_pair(xb, row, off))
572 return -1;
573 break;
574 default:
575 vi_back(mv);
576 return 0;
577 }
578 return mv;
579 }
580
581 static void swap(int *a, int *b)
582 {
583 int t = *a;
584 *a = *b;
585 *b = t;
586 }
587
588 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
589 {
590 struct sbuf *sb;
591 char *s1, *s2, *s3;
592 if (r1 == r2)
593 return uc_sub(lbuf_get(lb, r1), o1, o2);
594 sb = sbuf_make();
595 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
596 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
597 s2 = lbuf_cp(lb, r1 + 1, r2);
598 sbuf_str(sb, s1);
599 sbuf_str(sb, s2);
600 sbuf_str(sb, s3);
601 free(s1);
602 free(s2);
603 free(s3);
604 return sbuf_done(sb);
605 }
606
607 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
608 {
609 char *region;
610 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
611 reg_put(vi_ybuf, region, lnmode);
612 free(region);
613 xrow = r1;
614 xoff = lnmode ? xoff : o1;
615 }
616
617 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
618 {
619 char *pref, *post;
620 char *region;
621 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
622 reg_put(vi_ybuf, region, lnmode);
623 free(region);
624 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
625 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
626 if (!lnmode) {
627 struct sbuf *sb = sbuf_make();
628 sbuf_str(sb, pref);
629 sbuf_str(sb, post);
630 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
631 sbuf_free(sb);
632 } else {
633 lbuf_edit(xb, NULL, r1, r2 + 1);
634 }
635 xrow = r1;
636 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
637 free(pref);
638 free(post);
639 }
640
641 static int linecount(char *s)
642 {
643 int n;
644 for (n = 0; s; n++)
645 if ((s = strchr(s, '\n')))
646 s++;
647 return n;
648 }
649
650 static int charcount(char *text, char *post)
651 {
652 int tlen = strlen(text);
653 int plen = strlen(post);
654 char *nl = text;
655 int i;
656 if (tlen < plen)
657 return 0;
658 for (i = 0; i < tlen - plen; i++)
659 if (text[i] == '\n')
660 nl = text + i + 1;
661 return uc_slen(nl) - uc_slen(post);
662 }
663
664 static char *vi_input(char *pref, char *post, int *row, int *off)
665 {
666 char *rep = led_input(pref, post, &xkmap, ex_filetype());
667 if (!rep)
668 return NULL;
669 *row = linecount(rep) - 1;
670 *off = charcount(rep, post) - 1;
671 if (*off < 0)
672 *off = 0;
673 return rep;
674 }
675
676 static char *vi_indents(char *ln)
677 {
678 struct sbuf *sb = sbuf_make();
679 while (xai && ln && (*ln == ' ' || *ln == '\t'))
680 sbuf_chr(sb, *ln++);
681 return sbuf_done(sb);
682 }
683
684 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
685 {
686 char *region;
687 int row, off;
688 char *rep;
689 char *pref, *post;
690 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
691 reg_put(vi_ybuf, region, lnmode);
692 free(region);
693 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
694 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
695 vi_drawrm(r1, r2, 0);
696 rep = vi_input(pref, post, &row, &off);
697 if (rep) {
698 lbuf_edit(xb, rep, r1, r2 + 1);
699 xrow = r1 + row - 1;
700 xoff = off;
701 free(rep);
702 }
703 free(pref);
704 free(post);
705 }
706
707 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
708 {
709 char *pref, *post;
710 char *region, *s;
711 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
712 s = region;
713 while (*s) {
714 int c = (unsigned char) s[0];
715 if (c <= 0x7f) {
716 if (cmd == 'u')
717 s[0] = tolower(c);
718 if (cmd == 'U')
719 s[0] = toupper(c);
720 if (cmd == '~')
721 s[0] = islower(c) ? toupper(c) : tolower(c);
722 }
723 s = uc_next(s);
724 }
725 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
726 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
727 if (!lnmode) {
728 struct sbuf *sb = sbuf_make();
729 sbuf_str(sb, pref);
730 sbuf_str(sb, region);
731 sbuf_str(sb, post);
732 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
733 sbuf_free(sb);
734 } else {
735 lbuf_edit(xb, region, r1, r2 + 1);
736 }
737 xrow = r2;
738 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
739 free(region);
740 free(pref);
741 free(post);
742 }
743
744 static void vi_pipe(int r1, int r2)
745 {
746 char *text;
747 char *rep;
748 int kmap = 0;
749 char *cmd = vi_prompt("!", &kmap);
750 if (!cmd)
751 return;
752 text = lbuf_cp(xb, r1, r2 + 1);
753 rep = cmd_pipe(cmd, text, 1, 1);
754 if (rep)
755 lbuf_edit(xb, rep, r1, r2 + 1);
756 free(cmd);
757 free(text);
758 free(rep);
759 }
760
761 static void vi_shift(int r1, int r2, int dir)
762 {
763 struct sbuf *sb;
764 char *ln;
765 int i;
766 for (i = r1; i <= r2; i++) {
767 if (!(ln = lbuf_get(xb, i)))
768 continue;
769 sb = sbuf_make();
770 if (dir > 0)
771 sbuf_chr(sb, '\t');
772 else
773 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
774 sbuf_str(sb, ln);
775 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
776 sbuf_free(sb);
777 }
778 xrow = r1;
779 xoff = lbuf_indents(xb, xrow);
780 }
781
782 static int vc_motion(int cmd)
783 {
784 int r1 = xrow, r2 = xrow; /* region rows */
785 int o1 = xoff, o2 = xoff; /* visual region columns */
786 int lnmode = 0; /* line-based region */
787 int mv;
788 vi_arg2 = vi_prefix();
789 if (vi_arg2 < 0)
790 return 1;
791 o1 = ren_noeol(lbuf_get(xb, r1), o1);
792 o2 = o1;
793 if ((mv = vi_motionln(&r2, cmd))) {
794 o2 = -1;
795 } else if (!(mv = vi_motion(&r2, &o2))) {
796 vi_read();
797 return 1;
798 }
799 if (mv < 0)
800 return 1;
801 lnmode = o2 < 0;
802 if (lnmode) {
803 o1 = 0;
804 o2 = lbuf_eol(xb, r2);
805 }
806 if (r1 > r2) {
807 swap(&r1, &r2);
808 swap(&o1, &o2);
809 }
810 if (r1 == r2 && o1 > o2)
811 swap(&o1, &o2);
812 o1 = ren_noeol(lbuf_get(xb, r1), o1);
813 if (!lnmode && strchr("fFtTeE%", mv))
814 if (o2 < lbuf_eol(xb, r2))
815 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
816 if (cmd == 'y')
817 vi_yank(r1, o1, r2, o2, lnmode);
818 if (cmd == 'd')
819 vi_delete(r1, o1, r2, o2, lnmode);
820 if (cmd == 'c')
821 vi_change(r1, o1, r2, o2, lnmode);
822 if (cmd == '~' || cmd == 'u' || cmd == 'U')
823 vi_case(r1, o1, r2, o2, lnmode, cmd);
824 if (cmd == '!')
825 vi_pipe(r1, r2);
826 if (cmd == '>' || cmd == '<')
827 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
828 return 0;
829 }
830
831 static int vc_insert(int cmd)
832 {
833 char *pref, *post;
834 char *ln = lbuf_get(xb, xrow);
835 int row, off = 0;
836 char *rep;
837 if (cmd == 'I')
838 xoff = lbuf_indents(xb, xrow);
839 if (cmd == 'A')
840 xoff = lbuf_eol(xb, xrow);
841 xoff = ren_noeol(ln, xoff);
842 if (cmd == 'o')
843 xrow += 1;
844 if (cmd == 'i' || cmd == 'I')
845 off = xoff;
846 if (cmd == 'a' || cmd == 'A')
847 off = xoff + 1;
848 if (ln && ln[0] == '\n')
849 off = 0;
850 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
851 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
852 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
853 rep = vi_input(pref, post, &row, &off);
854 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
855 lbuf_edit(xb, "\n", 0, 0);
856 if (rep) {
857 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
858 xrow += row - 1;
859 xoff = off;
860 free(rep);
861 }
862 free(pref);
863 free(post);
864 return !rep;
865 }
866
867 static int vc_put(int cmd)
868 {
869 int cnt = MAX(1, vi_arg1);
870 int lnmode;
871 char *buf = reg_get(vi_ybuf, &lnmode);
872 int i;
873 if (!buf)
874 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
875 if (!buf || !buf[0])
876 return 1;
877 if (lnmode) {
878 struct sbuf *sb = sbuf_make();
879 for (i = 0; i < cnt; i++)
880 sbuf_str(sb, buf);
881 if (!lbuf_len(xb))
882 lbuf_edit(xb, "\n", 0, 0);
883 if (cmd == 'p')
884 xrow++;
885 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
886 xoff = lbuf_indents(xb, xrow);
887 sbuf_free(sb);
888 } else {
889 struct sbuf *sb = sbuf_make();
890 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
891 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
892 char *s = uc_sub(ln, 0, off);
893 sbuf_str(sb, s);
894 free(s);
895 for (i = 0; i < cnt; i++)
896 sbuf_str(sb, buf);
897 s = uc_sub(ln, off, -1);
898 sbuf_str(sb, s);
899 free(s);
900 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
901 xoff = off + uc_slen(buf) * cnt - 1;
902 sbuf_free(sb);
903 }
904 return 0;
905 }
906
907 static int join_spaces(char *prev, char *next)
908 {
909 int prevlen = strlen(prev);
910 if (!prev[0])
911 return 0;
912 if (prev[prevlen - 1] == ' ' || next[0] == ')')
913 return 0;
914 return prev[prevlen - 1] == '.' ? 2 : 1;
915 }
916
917 static int vc_join(void)
918 {
919 struct sbuf *sb;
920 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
921 int beg = xrow;
922 int end = xrow + cnt;
923 int off = 0;
924 int i;
925 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
926 return 1;
927 sb = sbuf_make();
928 for (i = beg; i < end; i++) {
929 char *ln = lbuf_get(xb, i);
930 char *lnend = strchr(ln, '\n');
931 int spaces;
932 if (i > beg)
933 while (ln[0] == ' ' || ln[0] == '\t')
934 ln++;
935 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
936 off = uc_slen(sbuf_buf(sb));
937 while (spaces--)
938 sbuf_chr(sb, ' ');
939 sbuf_mem(sb, ln, lnend - ln);
940 }
941 sbuf_chr(sb, '\n');
942 lbuf_edit(xb, sbuf_buf(sb), beg, end);
943 xoff = off;
944 sbuf_free(sb);
945 return 0;
946 }
947
948 static int vi_scrollforward(int cnt)
949 {
950 if (xtop >= lbuf_len(xb) - 1)
951 return 1;
952 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
953 xrow = MAX(xrow, xtop);
954 return 0;
955 }
956
957 static int vi_scrollbackward(int cnt)
958 {
959 if (xtop == 0)
960 return 1;
961 xtop = MAX(0, xtop - cnt);
962 xrow = MIN(xrow, xtop + xrows - 1);
963 return 0;
964 }
965
966 static void vc_status(void)
967 {
968 int col = vi_off2col(xb, xrow, xoff);
969 snprintf(vi_msg, sizeof(vi_msg),
970 "\"%s\"%c %d lines L%d C%d\n",
971 ex_path()[0] ? ex_path() : "unnamed",
972 lbuf_modified(xb) ? '*' : ' ',
973 lbuf_len(xb), xrow + 1,
974 ren_cursor(lbuf_get(xb, xrow), col) + 1);
975 }
976
977 static void vc_charinfo(void)
978 {
979 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
980 if (c) {
981 char cbuf[8] = "";
982 memcpy(cbuf, c, uc_len(c));
983 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
984 }
985 }
986
987 static int vc_replace(void)
988 {
989 int cnt = MAX(1, vi_arg1);
990 char *cs = vi_char();
991 char *ln = lbuf_get(xb, xrow);
992 struct sbuf *sb;
993 char *pref, *post;
994 char *s;
995 int off, i;
996 if (!ln || !cs)
997 return 1;
998 off = ren_noeol(ln, xoff);
999 s = uc_chr(ln, off);
1000 for (i = 0; s[0] != '\n' && i < cnt; i++)
1001 s = uc_next(s);
1002 if (i < cnt)
1003 return 1;
1004 pref = uc_sub(ln, 0, off);
1005 post = uc_sub(ln, off + cnt, -1);
1006 sb = sbuf_make();
1007 sbuf_str(sb, pref);
1008 for (i = 0; i < cnt; i++)
1009 sbuf_str(sb, cs);
1010 sbuf_str(sb, post);
1011 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1012 off += cnt - 1;
1013 xoff = off;
1014 sbuf_free(sb);
1015 free(pref);
1016 free(post);
1017 return 0;
1018 }
1019
1020 static char rep_cmd[4096]; /* the last command */
1021 static int rep_len;
1022
1023 static void vc_repeat(void)
1024 {
1025 int i;
1026 for (i = 0; i < MAX(1, vi_arg1); i++)
1027 term_push(rep_cmd, rep_len);
1028 }
1029
1030 static void vc_execute(void)
1031 {
1032 static int exec_buf = -1;
1033 int lnmode;
1034 int c = vi_read();
1035 char *buf = NULL;
1036 int i;
1037 if (TK_INT(c))
1038 return;
1039 if (c == '@')
1040 c = exec_buf;
1041 exec_buf = c;
1042 if (exec_buf >= 0)
1043 buf = reg_get(exec_buf, &lnmode);
1044 if (buf)
1045 for (i = 0; i < MAX(1, vi_arg1); i++)
1046 term_push(buf, strlen(buf));
1047 }
1048
1049 static void sigwinch(int signo)
1050 {
1051 vi_back(TK_CTL('l'));
1052 vi_back(TK_CTL('c'));
1053 }
1054
1055 static void vi(void)
1056 {
1057 int xcol;
1058 int mark;
1059 char *ln;
1060 int kmap = 0;
1061 signal(SIGWINCH, sigwinch);
1062 xtop = MAX(0, xrow - xrows / 2);
1063 xoff = 0;
1064 xcol = vi_off2col(xb, xrow, xoff);
1065 vi_drawagain(xcol, 0);
1066 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1067 while (!xquit) {
1068 int mod = 0; /* screen should be redrawn (1: the whole screen, 2: the current line) */
1069 int nrow = xrow;
1070 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1071 int otop = xtop;
1072 int oleft = xleft;
1073 int orow = xrow;
1074 int mv, n;
1075 term_cmd(&n);
1076 vi_arg2 = 0;
1077 vi_ybuf = vi_yankbuf();
1078 vi_arg1 = vi_prefix();
1079 if (!vi_ybuf)
1080 vi_ybuf = vi_yankbuf();
1081 mv = vi_motion(&nrow, &noff);
1082 if (mv > 0) {
1083 if (strchr("\'`GHML/?{}[]nN", mv) ||
1084 (mv == '%' && noff < 0)) {
1085 lbuf_mark(xb, '\'', xrow, xoff);
1086 lbuf_mark(xb, '`', xrow, xoff);
1087 }
1088 xrow = nrow;
1089 if (noff < 0 && !strchr("jk", mv))
1090 noff = lbuf_indents(xb, xrow);
1091 if (strchr("jk", mv))
1092 noff = vi_col2off(xb, xrow, xcol);
1093 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1094 if (!strchr("|jk", mv))
1095 xcol = vi_off2col(xb, xrow, xoff);
1096 if (mv == '|')
1097 xcol = vi_pcol;
1098 } else if (mv == 0) {
1099 char *cmd;
1100 int c = vi_read();
1101 int k = 0;
1102 if (c <= 0)
1103 continue;
1104 lbuf_mark(xb, '*', xrow, xoff);
1105 switch (c) {
1106 case TK_CTL('b'):
1107 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1108 break;
1109 xoff = lbuf_indents(xb, xrow);
1110 mod = 1;
1111 break;
1112 case TK_CTL('f'):
1113 if (vi_scrollforward(MAX(1, vi_arg1) * (xrows - 1)))
1114 break;
1115 xoff = lbuf_indents(xb, xrow);
1116 mod = 1;
1117 break;
1118 case TK_CTL('e'):
1119 if (vi_scrollforward(MAX(1, vi_arg1)))
1120 break;
1121 xoff = vi_col2off(xb, xrow, xcol);
1122 break;
1123 case TK_CTL('y'):
1124 if (vi_scrollbackward(MAX(1, vi_arg1)))
1125 break;
1126 xoff = vi_col2off(xb, xrow, xcol);
1127 break;
1128 case TK_CTL('u'):
1129 if (xrow == 0)
1130 break;
1131 if (vi_arg1)
1132 vi_scroll = vi_arg1;
1133 n = vi_scroll ? vi_scroll : xrows / 2;
1134 xrow = MAX(0, xrow - n);
1135 if (xtop > 0)
1136 xtop = MAX(0, xtop - n);
1137 xoff = lbuf_indents(xb, xrow);
1138 mod = 1;
1139 break;
1140 case TK_CTL('d'):
1141 if (xrow == lbuf_len(xb) - 1)
1142 break;
1143 if (vi_arg1)
1144 vi_scroll = vi_arg1;
1145 n = vi_scroll ? vi_scroll : xrows / 2;
1146 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1147 if (xtop < lbuf_len(xb) - xrows)
1148 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1149 xoff = lbuf_indents(xb, xrow);
1150 mod = 1;
1151 break;
1152 case TK_CTL('z'):
1153 term_pos(xrows, 0);
1154 term_suspend();
1155 mod = 1;
1156 break;
1157 case 'u':
1158 if (!lbuf_undo(xb)) {
1159 lbuf_jump(xb, '*', &xrow, &xoff);
1160 mod = 1;
1161 } else {
1162 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1163 }
1164 break;
1165 case TK_CTL('r'):
1166 if (!lbuf_redo(xb)) {
1167 lbuf_jump(xb, '*', &xrow, &xoff);
1168 mod = 1;
1169 } else {
1170 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1171 }
1172 break;
1173 case TK_CTL('g'):
1174 vc_status();
1175 break;
1176 case TK_CTL('^'):
1177 ex_command("e #");
1178 mod = 1;
1179 break;
1180 case ':':
1181 ln = vi_prompt(":", &kmap);
1182 if (ln && ln[0]) {
1183 ex_command(ln);
1184 mod = 1;
1185 }
1186 free(ln);
1187 if (xquit)
1188 continue;
1189 break;
1190 case 'c':
1191 case 'd':
1192 case 'y':
1193 case '!':
1194 case '>':
1195 case '<':
1196 if (!vc_motion(c))
1197 mod = 1;
1198 break;
1199 case 'i':
1200 case 'I':
1201 case 'a':
1202 case 'A':
1203 case 'o':
1204 case 'O':
1205 if (!vc_insert(c))
1206 mod = 1;
1207 break;
1208 case 'J':
1209 if (!vc_join())
1210 mod = 1;
1211 break;
1212 case TK_CTL('l'):
1213 term_done();
1214 term_init();
1215 mod = 1;
1216 break;
1217 case 'm':
1218 if ((mark = vi_read()) > 0 && islower(mark))
1219 lbuf_mark(xb, mark, xrow, xoff);
1220 break;
1221 case 'p':
1222 case 'P':
1223 if (!vc_put(c))
1224 mod = 1;
1225 break;
1226 case 'z':
1227 k = vi_read();
1228 switch (k) {
1229 case '\n':
1230 xtop = vi_arg1 ? vi_arg1 : xrow;
1231 break;
1232 case '.':
1233 n = vi_arg1 ? vi_arg1 : xrow;
1234 xtop = MAX(0, n - xrows / 2);
1235 break;
1236 case '-':
1237 n = vi_arg1 ? vi_arg1 : xrow;
1238 xtop = MAX(0, n - xrows + 1);
1239 break;
1240 case 'l':
1241 case 'r':
1242 xtd = k == 'r' ? -1 : +1;
1243 break;
1244 case 'L':
1245 case 'R':
1246 xtd = k == 'R' ? -2 : +2;
1247 break;
1248 case 'e':
1249 case 'f':
1250 xkmap = k == 'e' ? 0 : xkmap_alt;
1251 break;
1252 }
1253 mod = 1;
1254 break;
1255 case 'g':
1256 k = vi_read();
1257 if (k == '~' || k == 'u' || k == 'U')
1258 if (!vc_motion(k))
1259 mod = 2;
1260 if (k == 'a')
1261 vc_charinfo();
1262 break;
1263 case 'x':
1264 vi_back(' ');
1265 if (!vc_motion('d'))
1266 mod = 2;
1267 break;
1268 case 'X':
1269 vi_back(TK_CTL('h'));
1270 if (!vc_motion('d'))
1271 mod = 2;
1272 break;
1273 case 'C':
1274 vi_back('$');
1275 if (!vc_motion('c'))
1276 mod = 1;
1277 break;
1278 case 'D':
1279 vi_back('$');
1280 if (!vc_motion('d'))
1281 mod = 2;
1282 break;
1283 case 'r':
1284 if (!vc_replace())
1285 mod = 2;
1286 break;
1287 case 's':
1288 vi_back(' ');
1289 if (!vc_motion('c'))
1290 mod = 1;
1291 break;
1292 case 'S':
1293 vi_back('c');
1294 if (!vc_motion('c'))
1295 mod = 1;
1296 break;
1297 case 'Y':
1298 vi_back('y');
1299 vc_motion('y');
1300 break;
1301 case 'q':
1302 k = vi_read();
1303 if (k == 'q')
1304 ex_command("q");
1305 break;
1306 case 'Z':
1307 k = vi_read();
1308 if (k == 'Z')
1309 ex_command("x");
1310 break;
1311 case '~':
1312 vi_back(' ');
1313 if (!vc_motion('~'))
1314 mod = 2;
1315 break;
1316 case '.':
1317 vc_repeat();
1318 break;
1319 case '@':
1320 vc_execute();
1321 break;
1322 default:
1323 continue;
1324 }
1325 cmd = term_cmd(&n);
1326 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1327 (c == 'g' && strchr("uU~", k))) {
1328 if (n < sizeof(rep_cmd)) {
1329 memcpy(rep_cmd, cmd, n);
1330 rep_len = n;
1331 }
1332 }
1333 }
1334 if (xrow < 0 || xrow >= lbuf_len(xb))
1335 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1336 if (xtop > xrow)
1337 xtop = xtop - xrows / 2 > xrow ?
1338 MAX(0, xrow - xrows / 2) : xrow;
1339 if (xtop + xrows <= xrow)
1340 xtop = xtop + xrows + xrows / 2 <= xrow ?
1341 xrow - xrows / 2 : xrow - xrows + 1;
1342 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1343 if (mod)
1344 xcol = vi_off2col(xb, xrow, xoff);
1345 if (xcol >= xleft + xcols)
1346 xleft = xcol - xcols / 2;
1347 if (xcol < xleft)
1348 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1349 vi_wait();
1350 if (mod || xleft != oleft) {
1351 vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
1352 } else {
1353 if (xtop != otop)
1354 vi_drawupdate(xcol, otop);
1355 if (xhll && xrow != orow && orow >= xtop && orow < xtop + xrows)
1356 vi_drawrow(orow);
1357 if (xhll && xrow != orow)
1358 vi_drawrow(xrow);
1359 }
1360 if (vi_msg[0])
1361 vi_drawmsg();
1362 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1363 ren_cursor(lbuf_get(xb, xrow), xcol)));
1364 lbuf_modified(xb);
1365 }
1366 term_pos(xrows, 0);
1367 term_kill();
1368 }
1369
1370 int main(int argc, char *argv[])
1371 {
1372 int i;
1373 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1374 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1375 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1376 if (argv[i][1] == 's')
1377 xled = 0;
1378 if (argv[i][1] == 'e')
1379 xvis = 0;
1380 if (argv[i][1] == 'v')
1381 xvis = 1;
1382 }
1383 dir_init();
1384 syn_init();
1385 if (xled || xvis)
1386 term_init();
1387 if (!ex_init(argv + i)) {
1388 if (xvis)
1389 vi();
1390 else
1391 ex();
1392 ex_done();
1393 }
1394 if (xled || xvis)
1395 term_done();
1396 reg_done();
1397 syn_done();
1398 dir_done();
1399 return 0;
1400 }