vi: collect ex command output before printing
[neatvi.git] / vi.c
blob7e69f4e645400773391f307691a59a8f414620c4
1 /*
2 * NEATVI Editor
4 * Copyright (C) 2015-2023 Ali Gholami Rudi <ali at rudi dot ir>
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.
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.
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 <unistd.h>
25 #include "vi.h"
27 static char vi_msg[EXLEN]; /* current message */
28 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
29 static int vi_charcmd; /* the character finding command */
30 static int vi_arg1, vi_arg2; /* the first and second arguments */
31 static int vi_ybuf; /* current yank buffer */
32 static int vi_pcol; /* the column requested by | command */
33 static int vi_printed; /* ex_print() calls since the last command */
34 static int vi_scroll; /* scroll amount for ^f and ^d*/
35 static int vi_soset, vi_so; /* search offset; 1 in "/kw/1" */
36 static int w_cnt = 1; /* window count */
37 static int w_cur; /* active window identifier */
38 static int w_tmp; /* temporary window */
39 static char *w_path; /* saved window path */
40 static int w_row, w_off, w_top, w_left; /* saved window configuration */
42 static void vc_status(void);
44 static void vi_wait(void)
46 if (vi_printed > 1 || vi_printed < 0) {
47 if (vi_printed < 0)
48 term_window(0, term_rowx() - 1);
49 term_pos(xrows, 0);
50 free(led_prompt("[enter to continue]", "", &xkmap, xhl ? "---" : "___"));
51 vi_msg[0] = '\0';
53 vi_printed = 0;
56 static void vi_drawmsg(void)
58 int oleft = xleft;
59 xleft = 0;
60 led_printmsg(vi_msg[0] ? vi_msg : "\n", xrows, xhl ? "---" : "___");
61 vi_msg[0] = '\0';
62 xleft = oleft;
65 static void vi_drawrow(int row)
67 char *s = lbuf_get(xb, row);
68 if (xhll && row == xrow)
69 syn_context(conf_hlline());
70 led_print(s ? s : (row ? "~" : ""), row - xtop, xhl ? ex_filetype() : "");
71 syn_context(0);
74 /* redraw the screen */
75 static void vi_drawagain(int xcol, int lineonly)
77 int i;
78 term_record();
79 for (i = xtop; i < xtop + xrows; i++)
80 if (!lineonly || i == xrow)
81 vi_drawrow(i);
82 vi_drawmsg();
83 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
84 term_commit();
87 /* update the screen */
88 static void vi_drawupdate(int xcol, int otop)
90 int i = 0;
91 if (otop != xtop) {
92 term_record();
93 term_pos(0, 0);
94 term_room(otop - xtop);
95 if (xtop > otop) {
96 int n = MIN(xtop - otop, xrows);
97 for (i = 0; i < n; i++)
98 vi_drawrow(xtop + xrows - n + i);
99 } else {
100 int n = MIN(otop - xtop, xrows);
101 for (i = 0; i < n; i++)
102 vi_drawrow(xtop + i);
104 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
105 term_commit();
107 vi_drawmsg();
108 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
111 /* update the screen by removing lines r1 to r2 before an input command */
112 static void vi_drawrm(int r1, int r2, int newln)
114 r1 = MIN(MAX(r1, xtop), xtop + xrows - 1);
115 r2 = MIN(MAX(r2, xtop), xtop + xrows - 1);
116 term_pos(r1 - xtop, 0);
117 term_room(r1 - r2 + newln);
120 static int vi_switch(int id)
122 int beg = 0;
123 int cnt = term_rowx();
124 if (id >= w_cnt)
125 return 1;
126 if (id != w_cur) {
127 char cmd[1024];
128 char *old = w_path && w_path[0] ? w_path : "/";
129 int row = w_row, off = w_off, top = w_top, left = w_left;
130 char *ec = "ew";
131 if (w_path && strcmp(w_path, ex_path()) == 0)
132 ec = "e";
133 snprintf(cmd, sizeof(cmd), "%s! %s", ec, old);
134 free(w_path);
135 w_path = uc_dup(ex_path());
136 w_row = xrow, w_off = xoff, w_top = xtop, w_left = xleft;
137 ex_command(cmd);
138 xrow = row, xoff = off, xtop = top, xleft = left;
140 if (w_cnt == 2) {
141 int half = cnt / 2;
142 beg = id == 0 ? 0 : half;
143 cnt = id == 0 ? half : term_rowx() - half;
145 term_window(beg, cnt - 1);
146 w_cur = id;
147 return 0;
150 static int vi_wsplit(void)
152 if (w_cnt != 1)
153 return 1;
154 free(w_path);
155 w_cnt = 2;
156 w_path = uc_dup(ex_path());
157 w_row = xrow, w_off = xoff, w_top = xtop, w_left = xleft;
158 return vi_switch(0);
161 static int vi_wonly(void)
163 if (w_cnt != 2)
164 return 1;
165 w_cnt = 1;
166 w_cur = 0;
167 return vi_switch(0);
170 static int vi_wclose(void)
172 if (w_cnt != 2)
173 return 1;
174 vi_switch(1 - w_cur);
175 return vi_wonly();
178 static int vi_wswap(void)
180 if (w_cnt != 2)
181 return 1;
182 w_cur = 1 - w_cur;
183 return 0;
186 static void vi_wfix(void)
188 if (xrow < 0 || xrow >= lbuf_len(xb))
189 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
190 if (xtop > xrow)
191 xtop = xtop - xrows / 2 > xrow ?
192 MAX(0, xrow - xrows / 2) : xrow;
193 if (xtop + xrows <= xrow)
194 xtop = xtop + xrows + xrows / 2 <= xrow ?
195 xrow - xrows / 2 : xrow - xrows + 1;
196 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
199 static int vi_buf[128];
200 static int vi_buflen;
202 static int vi_read(void)
204 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
207 static void vi_back(int c)
209 if (vi_buflen < sizeof(vi_buf))
210 vi_buf[vi_buflen++] = c;
213 static char *vi_char(void)
215 return led_read(&xkmap);
218 static char *vi_prompt(char *msg, int *kmap)
220 char *r, *s;
221 term_pos(xrows, led_pos(msg, 0));
222 term_kill();
223 s = led_prompt(msg, "", kmap, xhl ? "---" : "___");
224 if (!s)
225 return NULL;
226 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
227 free(s);
228 return r;
231 /* read an ex input line */
232 char *ex_read(char *msg)
234 struct sbuf *sb;
235 int c;
236 if (xled) {
237 char *s = led_prompt(msg, "", &xkmap, xhl ? "---" : "___");
238 if (s)
239 term_chr('\n');
240 return s;
242 sb = sbuf_make();
243 while ((c = getchar()) != EOF && c != '\n')
244 sbuf_chr(sb, c);
245 if (c == EOF) {
246 sbuf_free(sb);
247 return NULL;
249 return sbuf_done(sb);
252 /* show an ex message */
253 void ex_show(char *msg)
255 if (xvis) {
256 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
257 } else if (xled) {
258 led_print(msg, -1, xhl ? "---" : "___");
259 term_chr('\n');
260 } else {
261 printf("%s", msg);
265 /* print an ex output line */
266 void ex_print(char *line)
268 if (xvis) {
269 if (line && vi_printed == 0)
270 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
271 if (line && vi_printed == 1)
272 led_print(vi_msg, xrows - 1, xhl ? "-ex" : "");
273 if (vi_printed)
274 term_chr('\n');
275 if (line && vi_printed)
276 led_print(line, xrows - 1, xhl ? "-ex" : "");
277 vi_printed += line != NULL ? 1 : -1000;
278 } else {
279 if (line)
280 ex_show(line);
284 static int vi_yankbuf(void)
286 int c = vi_read();
287 if (c == '"')
288 return vi_read();
289 vi_back(c);
290 return 0;
293 static int vi_prefix(void)
295 int n = 0;
296 int c = vi_read();
297 if ((c >= '1' && c <= '9')) {
298 while (isdigit(c)) {
299 n = n * 10 + c - '0';
300 c = vi_read();
303 vi_back(c);
304 return n;
307 static int vi_col2off(struct lbuf *lb, int row, int col)
309 char *ln = lbuf_get(lb, row);
310 return ln ? ren_off(ln, col) : 0;
313 static int vi_off2col(struct lbuf *lb, int row, int off)
315 char *ln = lbuf_get(lb, row);
316 return ln ? ren_pos(ln, off) : 0;
319 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
321 int o = *off + dir;
322 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
323 return 1;
324 *off = o;
325 return 0;
328 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
330 char *ln = lbuf_get(lb, *row);
331 int col = ln ? ren_pos(ln, *off) : 0;
332 int o = ln ? ren_next(ln, col, dir) : -1;
333 if (o < 0)
334 return -1;
335 *off = ren_off(ln, o);
336 return 0;
339 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
341 if (cs != vi_charlast)
342 strcpy(vi_charlast, cs);
343 vi_charcmd = cmd;
344 return lbuf_findchar(lb, cs, cmd, n, row, off);
347 static int vi_search(int cmd, int cnt, int *row, int *off)
349 char *kwd;
350 int r = *row;
351 int o = *off;
352 char *failed = NULL;
353 int len = 0;
354 int i, dir;
355 if (cmd == '/' || cmd == '?') {
356 char sign[4] = {cmd};
357 struct sbuf *sb;
358 char *kw = vi_prompt(sign, &xkmap);
359 char *re;
360 if (!kw)
361 return 1;
362 sb = sbuf_make();
363 sbuf_chr(sb, cmd);
364 sbuf_str(sb, kw);
365 free(kw);
366 kw = sbuf_buf(sb);
367 if ((re = re_read(&kw))) {
368 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
369 while (isspace(*kw))
370 kw++;
371 vi_soset = !!kw[0];
372 vi_so = atoi(kw);
373 free(re);
375 sbuf_free(sb);
377 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
378 return 1;
379 dir = cmd == 'N' ? -dir : dir;
380 o = *off;
381 for (i = 0; i < cnt; i++) {
382 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
383 failed = " not found";
384 break;
386 if (i + 1 < cnt && cmd == '/')
387 o += len;
389 if (!failed) {
390 *row = r;
391 *off = o;
392 if (vi_soset) {
393 *off = -1;
394 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
395 failed = " bad offset";
396 else
397 *row += vi_so;
400 if (failed != NULL)
401 snprintf(vi_msg, sizeof(vi_msg), "/%s/%s", kwd, failed ? failed : "");
402 return failed != NULL;
405 /* read a line motion */
406 static int vi_motionln(int *row, int cmd)
408 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
409 int c = vi_read();
410 int mark, mark_row, mark_off;
411 switch (c) {
412 case '\n':
413 case '+':
414 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
415 break;
416 case '-':
417 *row = MAX(*row - cnt, 0);
418 break;
419 case '_':
420 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
421 break;
422 case '\'':
423 if ((mark = vi_read()) <= 0)
424 return -1;
425 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
426 return -1;
427 *row = mark_row;
428 break;
429 case 'j':
430 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
431 break;
432 case 'k':
433 *row = MAX(*row - cnt, 0);
434 break;
435 case 'G':
436 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
437 break;
438 case 'H':
439 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
440 break;
441 case 'L':
442 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
443 break;
444 case 'M':
445 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
446 break;
447 default:
448 if (c == cmd) {
449 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
450 break;
452 if (c == '%' && (vi_arg1 || vi_arg2)) {
453 if (cnt > 100)
454 return -1;
455 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
456 break;
458 vi_back(c);
459 return 0;
461 if (*row < 0)
462 *row = 0;
463 return c;
466 static int vi_curword(struct lbuf *lb, char *dst, int len, int row, int off, char *ext)
468 char *ln = lbuf_get(lb, row);
469 char *beg, *end;
470 if (!ln)
471 return 1;
472 beg = uc_chr(ln, ren_noeol(ln, off));
473 end = beg;
474 while (*end && (uc_kind(end) == 1 ||
475 strchr(ext, (unsigned char) end[0]) != NULL))
476 end = uc_next(end);
477 while (beg > ln && (uc_kind(uc_beg(ln, beg - 1)) == 1 ||
478 strchr(ext, (unsigned char) beg[-1]) != NULL))
479 beg = uc_beg(ln, beg - 1);
480 if (beg >= end)
481 return 1;
482 len = len - 1 < end - beg ? len - 1 : end - beg;
483 dst[len] = '\0';
484 memcpy(dst, beg, len);
485 return 0;
488 /* read a motion */
489 static int vi_motion(int *row, int *off)
491 char cw[120], kw[128];
492 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
493 char *ln = lbuf_get(xb, *row);
494 int dir = dir_context(ln ? ln : "");
495 int mark, mark_row, mark_off;
496 char *cs;
497 int mv;
498 int i;
499 if ((mv = vi_motionln(row, 0))) {
500 *off = -1;
501 return mv;
503 mv = vi_read();
504 switch (mv) {
505 case 'f':
506 if (!(cs = vi_char()))
507 return -1;
508 if (vi_findchar(xb, cs, mv, cnt, row, off))
509 return -1;
510 break;
511 case 'F':
512 if (!(cs = vi_char()))
513 return -1;
514 if (vi_findchar(xb, cs, mv, cnt, row, off))
515 return -1;
516 break;
517 case ';':
518 if (!vi_charlast[0])
519 return -1;
520 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
521 return -1;
522 break;
523 case ',':
524 if (!vi_charlast[0])
525 return -1;
526 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
527 return -1;
528 break;
529 case 'h':
530 for (i = 0; i < cnt; i++)
531 if (vi_nextcol(xb, -1 * dir, row, off))
532 break;
533 break;
534 case 'l':
535 for (i = 0; i < cnt; i++)
536 if (vi_nextcol(xb, +1 * dir, row, off))
537 break;
538 break;
539 case 't':
540 if (!(cs = vi_char()))
541 return -1;
542 if (vi_findchar(xb, cs, mv, cnt, row, off))
543 return -1;
544 break;
545 case 'T':
546 if (!(cs = vi_char()))
547 return -1;
548 if (vi_findchar(xb, cs, mv, cnt, row, off))
549 return -1;
550 break;
551 case 'B':
552 for (i = 0; i < cnt; i++)
553 if (lbuf_wordend(xb, 1, -1, row, off))
554 break;
555 break;
556 case 'E':
557 for (i = 0; i < cnt; i++)
558 if (lbuf_wordend(xb, 1, +1, row, off))
559 break;
560 break;
561 case 'W':
562 for (i = 0; i < cnt; i++)
563 if (lbuf_wordbeg(xb, 1, +1, row, off))
564 break;
565 break;
566 case 'b':
567 for (i = 0; i < cnt; i++)
568 if (lbuf_wordend(xb, 0, -1, row, off))
569 break;
570 break;
571 case 'e':
572 for (i = 0; i < cnt; i++)
573 if (lbuf_wordend(xb, 0, +1, row, off))
574 break;
575 break;
576 case 'w':
577 for (i = 0; i < cnt; i++)
578 if (lbuf_wordbeg(xb, 0, +1, row, off))
579 break;
580 break;
581 case '{':
582 for (i = 0; i < cnt; i++)
583 if (lbuf_paragraphbeg(xb, -1, row, off))
584 break;
585 break;
586 case '}':
587 for (i = 0; i < cnt; i++)
588 if (lbuf_paragraphbeg(xb, +1, row, off))
589 break;
590 break;
591 case '[':
592 if (vi_read() != '[')
593 return -1;
594 for (i = 0; i < cnt; i++)
595 if (lbuf_sectionbeg(xb, -1, conf_section(ex_filetype()), row, off))
596 break;
597 break;
598 case ']':
599 if (vi_read() != ']')
600 return -1;
601 for (i = 0; i < cnt; i++)
602 if (lbuf_sectionbeg(xb, +1, conf_section(ex_filetype()), row, off))
603 break;
604 break;
605 case '0':
606 *off = 0;
607 break;
608 case '^':
609 *off = lbuf_indents(xb, *row);
610 break;
611 case '$':
612 *off = lbuf_eol(xb, *row);
613 break;
614 case '|':
615 *off = vi_col2off(xb, *row, cnt - 1);
616 vi_pcol = cnt - 1;
617 break;
618 case '/':
619 if (vi_search(mv, cnt, row, off))
620 return -1;
621 break;
622 case '?':
623 if (vi_search(mv, cnt, row, off))
624 return -1;
625 break;
626 case 'n':
627 if (vi_search(mv, cnt, row, off))
628 return -1;
629 break;
630 case 'N':
631 if (vi_search(mv, cnt, row, off))
632 return -1;
633 break;
634 case TK_CTL('a'):
635 if (vi_curword(xb, cw, sizeof(cw), *row, *off, "") != 0)
636 return -1;
637 snprintf(kw, sizeof(kw), "\\<%s\\>", cw);
638 ex_kwdset(kw, +1);
639 vi_soset = 0;
640 if (vi_search('n', cnt, row, off))
641 return -1;
642 break;
643 case ' ':
644 for (i = 0; i < cnt; i++)
645 if (vi_nextoff(xb, +1, row, off))
646 break;
647 break;
648 case 127:
649 case TK_CTL('h'):
650 for (i = 0; i < cnt; i++)
651 if (vi_nextoff(xb, -1, row, off))
652 break;
653 break;
654 case '`':
655 if ((mark = vi_read()) <= 0)
656 return -1;
657 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
658 return -1;
659 *row = mark_row;
660 *off = mark_off;
661 break;
662 case '%':
663 if (lbuf_pair(xb, row, off))
664 return -1;
665 break;
666 default:
667 vi_back(mv);
668 return 0;
670 return mv;
673 static void swap(int *a, int *b)
675 int t = *a;
676 *a = *b;
677 *b = t;
680 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
682 struct sbuf *sb;
683 char *s1, *s2, *s3;
684 if (r1 == r2)
685 return uc_sub(lbuf_get(lb, r1), o1, o2);
686 sb = sbuf_make();
687 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
688 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
689 s2 = lbuf_cp(lb, r1 + 1, r2);
690 sbuf_str(sb, s1);
691 sbuf_str(sb, s2);
692 sbuf_str(sb, s3);
693 free(s1);
694 free(s2);
695 free(s3);
696 return sbuf_done(sb);
699 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
701 char *region;
702 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
703 reg_put(vi_ybuf, region, lnmode);
704 free(region);
705 xrow = r1;
706 xoff = lnmode ? xoff : o1;
709 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
711 char *pref, *post;
712 char *region;
713 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
714 reg_put(vi_ybuf, region, lnmode);
715 free(region);
716 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
717 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
718 if (!lnmode) {
719 char *line = uc_cat(pref, post);
720 lbuf_edit(xb, line, r1, r2 + 1);
721 free(line);
722 } else {
723 lbuf_edit(xb, NULL, r1, r2 + 1);
725 xrow = r1;
726 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
727 free(pref);
728 free(post);
731 static int linecount(char *s)
733 int n;
734 for (n = 0; s; n++)
735 if ((s = strchr(s, '\n')))
736 s++;
737 return n;
740 static int charcount(char *text, char *post)
742 int tlen = strlen(text);
743 int plen = strlen(post);
744 char *nl = text;
745 int i;
746 if (tlen < plen)
747 return 0;
748 for (i = 0; i < tlen - plen; i++)
749 if (text[i] == '\n')
750 nl = text + i + 1;
751 return uc_slen(nl) - uc_slen(post);
754 static char *vi_input(char *pref, char *post, int *row, int *off)
756 char *rep = led_input(pref, post, &xkmap, xhl ? ex_filetype() : "");
757 if (!rep)
758 return NULL;
759 *row = linecount(rep) - 1;
760 *off = charcount(rep, post) - 1;
761 if (*off < 0)
762 *off = 0;
763 return rep;
766 static char *vi_indents(char *ln)
768 struct sbuf *sb = sbuf_make();
769 while (xai && ln && (*ln == ' ' || *ln == '\t'))
770 sbuf_chr(sb, *ln++);
771 return sbuf_done(sb);
774 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
776 char *region;
777 int row, off;
778 char *rep;
779 char *pref, *post;
780 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
781 reg_put(vi_ybuf, region, lnmode);
782 free(region);
783 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
784 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
785 vi_drawrm(r1, r2, 0);
786 rep = vi_input(pref, post, &row, &off);
787 if (rep) {
788 lbuf_edit(xb, rep, r1, r2 + 1);
789 xrow = r1 + row - 1;
790 xoff = off;
791 free(rep);
793 free(pref);
794 free(post);
797 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
799 char *pref, *post;
800 char *region, *s;
801 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
802 s = region;
803 while (*s) {
804 int c = (unsigned char) s[0];
805 if (c <= 0x7f) {
806 if (cmd == 'u')
807 s[0] = tolower(c);
808 if (cmd == 'U')
809 s[0] = toupper(c);
810 if (cmd == '~')
811 s[0] = islower(c) ? toupper(c) : tolower(c);
813 s = uc_next(s);
815 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
816 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
817 if (!lnmode) {
818 struct sbuf *sb = sbuf_make();
819 sbuf_str(sb, pref);
820 sbuf_str(sb, region);
821 sbuf_str(sb, post);
822 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
823 sbuf_free(sb);
824 } else {
825 lbuf_edit(xb, region, r1, r2 + 1);
827 xrow = r2;
828 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
829 free(region);
830 free(pref);
831 free(post);
834 static void vi_pipe(int r1, int r2)
836 char *text;
837 char *rep;
838 int kmap = 0;
839 char *cmd = vi_prompt("!", &kmap);
840 if (!cmd)
841 return;
842 text = lbuf_cp(xb, r1, r2 + 1);
843 rep = cmd_pipe(cmd, text, 1);
844 if (rep)
845 lbuf_edit(xb, rep, r1, r2 + 1);
846 free(cmd);
847 free(text);
848 free(rep);
851 static void vi_shift(int r1, int r2, int dir)
853 struct sbuf *sb;
854 char *ln;
855 int i;
856 for (i = r1; i <= r2; i++) {
857 if (!(ln = lbuf_get(xb, i)))
858 continue;
859 sb = sbuf_make();
860 if (dir > 0) {
861 if (ln[0] != '\n')
862 sbuf_chr(sb, '\t');
863 } else {
864 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
866 sbuf_str(sb, ln);
867 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
868 sbuf_free(sb);
870 xrow = r1;
871 xoff = lbuf_indents(xb, xrow);
874 static int vc_motion(int cmd)
876 int r1 = xrow, r2 = xrow; /* region rows */
877 int o1 = xoff, o2 = xoff; /* visual region columns */
878 int lnmode = 0; /* line-based region */
879 int mv;
880 vi_arg2 = vi_prefix();
881 if (vi_arg2 < 0)
882 return 1;
883 o1 = ren_noeol(lbuf_get(xb, r1), o1);
884 o2 = o1;
885 if ((mv = vi_motionln(&r2, cmd))) {
886 o2 = -1;
887 } else if (!(mv = vi_motion(&r2, &o2))) {
888 vi_read();
889 return 1;
891 if (mv < 0)
892 return 1;
893 lnmode = o2 < 0;
894 if (lnmode) {
895 o1 = 0;
896 o2 = lbuf_eol(xb, r2);
898 if (r1 > r2) {
899 swap(&r1, &r2);
900 swap(&o1, &o2);
902 if (r1 == r2 && o1 > o2)
903 swap(&o1, &o2);
904 o1 = ren_noeol(lbuf_get(xb, r1), o1);
905 if (!lnmode && strchr("fFtTeE%", mv))
906 if (o2 < lbuf_eol(xb, r2))
907 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
908 if (cmd == 'y')
909 vi_yank(r1, o1, r2, o2, lnmode);
910 if (cmd == 'd')
911 vi_delete(r1, o1, r2, o2, lnmode);
912 if (cmd == 'c')
913 vi_change(r1, o1, r2, o2, lnmode);
914 if (cmd == '~' || cmd == 'u' || cmd == 'U')
915 vi_case(r1, o1, r2, o2, lnmode, cmd);
916 if (cmd == '>' || cmd == '<')
917 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
918 if (cmd == '!') {
919 if (mv == '{' || mv == '}')
920 if (lbuf_get(xb, r2) && lbuf_get(xb, r2)[0] == '\n' && r1 < r2)
921 r2--;
922 vi_pipe(r1, r2);
924 return 0;
927 static int vc_insert(int cmd)
929 char *pref, *post;
930 char *ln = lbuf_get(xb, xrow);
931 int row, off = 0;
932 char *rep;
933 if (cmd == 'I')
934 xoff = lbuf_indents(xb, xrow);
935 if (cmd == 'A')
936 xoff = lbuf_eol(xb, xrow);
937 xoff = ren_noeol(ln, xoff);
938 if (cmd == 'o')
939 xrow += 1;
940 if (cmd == 'i' || cmd == 'I')
941 off = xoff;
942 if (cmd == 'a' || cmd == 'A')
943 off = xoff + 1;
944 if (ln && ln[0] == '\n')
945 off = 0;
946 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
947 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
948 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
949 rep = vi_input(pref, post, &row, &off);
950 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
951 lbuf_edit(xb, "\n", 0, 0);
952 if (rep) {
953 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
954 xrow += row - 1;
955 xoff = off;
956 free(rep);
958 free(pref);
959 free(post);
960 return !rep;
963 static int vc_put(int cmd)
965 int cnt = MAX(1, vi_arg1);
966 int lnmode;
967 char *buf = reg_get(vi_ybuf, &lnmode);
968 int i;
969 if (!buf)
970 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty");
971 if (!buf || !buf[0])
972 return 1;
973 if (lnmode) {
974 struct sbuf *sb = sbuf_make();
975 for (i = 0; i < cnt; i++)
976 sbuf_str(sb, buf);
977 if (!lbuf_len(xb))
978 lbuf_edit(xb, "\n", 0, 0);
979 if (cmd == 'p')
980 xrow++;
981 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
982 xoff = lbuf_indents(xb, xrow);
983 sbuf_free(sb);
984 } else {
985 struct sbuf *sb = sbuf_make();
986 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
987 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
988 char *s = uc_sub(ln, 0, off);
989 sbuf_str(sb, s);
990 free(s);
991 for (i = 0; i < cnt; i++)
992 sbuf_str(sb, buf);
993 s = uc_sub(ln, off, -1);
994 sbuf_str(sb, s);
995 free(s);
996 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
997 xoff = off + uc_slen(buf) * cnt - 1;
998 sbuf_free(sb);
1000 return 0;
1003 static int join_spaces(char *prev, char *next)
1005 int prevlen = strlen(prev);
1006 if (!prev[0])
1007 return 0;
1008 if (prev[prevlen - 1] == ' ' || next[0] == ')')
1009 return 0;
1010 return prev[prevlen - 1] == '.' ? 2 : 1;
1013 static int vc_join(void)
1015 struct sbuf *sb;
1016 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
1017 int beg = xrow;
1018 int end = xrow + cnt;
1019 int off = 0;
1020 int i;
1021 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
1022 return 1;
1023 sb = sbuf_make();
1024 for (i = beg; i < end; i++) {
1025 char *ln = lbuf_get(xb, i);
1026 char *lnend = strchr(ln, '\n');
1027 int spaces;
1028 if (i > beg)
1029 while (ln[0] == ' ' || ln[0] == '\t')
1030 ln++;
1031 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
1032 off = uc_slen(sbuf_buf(sb));
1033 while (spaces--)
1034 sbuf_chr(sb, ' ');
1035 sbuf_mem(sb, ln, lnend - ln);
1037 sbuf_chr(sb, '\n');
1038 lbuf_edit(xb, sbuf_buf(sb), beg, end);
1039 xoff = off;
1040 sbuf_free(sb);
1041 return 0;
1044 static int vi_scrollforward(int cnt)
1046 if (xtop >= lbuf_len(xb) - 1)
1047 return 1;
1048 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
1049 xrow = MAX(xrow, xtop);
1050 return 0;
1053 static int vi_scrollbackward(int cnt)
1055 if (xtop == 0)
1056 return 1;
1057 xtop = MAX(0, xtop - cnt);
1058 xrow = MIN(xrow, xtop + xrows - 1);
1059 return 0;
1062 static void vc_status(void)
1064 int col = vi_off2col(xb, xrow, xoff);
1065 int win = w_tmp ? '-' : '=';
1066 snprintf(vi_msg, sizeof(vi_msg),
1067 "\"%s\"%c [%c%d] L%d C%d",
1068 ex_path()[0] ? ex_path() : "unnamed",
1069 lbuf_modified(xb) ? '*' : ' ',
1070 win, lbuf_len(xb), xrow + 1,
1071 ren_cursor(lbuf_get(xb, xrow), col) + 1);
1074 static void vc_charinfo(void)
1076 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
1077 if (c) {
1078 char cbuf[8] = "";
1079 memcpy(cbuf, c, uc_len(c));
1080 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x", cbuf, uc_code(c));
1084 static int vc_replace(void)
1086 int cnt = MAX(1, vi_arg1);
1087 char *cs = vi_char();
1088 char *ln = lbuf_get(xb, xrow);
1089 struct sbuf *sb;
1090 char *pref, *post;
1091 char *s;
1092 int off, i;
1093 if (!ln || !cs)
1094 return 1;
1095 off = ren_noeol(ln, xoff);
1096 s = uc_chr(ln, off);
1097 for (i = 0; s[0] != '\n' && i < cnt; i++)
1098 s = uc_next(s);
1099 if (i < cnt)
1100 return 1;
1101 pref = uc_sub(ln, 0, off);
1102 post = uc_sub(ln, off + cnt, -1);
1103 sb = sbuf_make();
1104 sbuf_str(sb, pref);
1105 for (i = 0; i < cnt; i++)
1106 sbuf_str(sb, cs);
1107 sbuf_str(sb, post);
1108 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1109 off += cnt - 1;
1110 xoff = off;
1111 sbuf_free(sb);
1112 free(pref);
1113 free(post);
1114 return 0;
1117 static void vi_marksave(void)
1119 lbuf_mark(xb, '\'', xrow, xoff);
1120 lbuf_mark(xb, '`', xrow, xoff);
1123 static int vc_definition(int newwin)
1125 char cw[256], kw[256];
1126 char *s, *ln;
1127 int r = 0, o = 0;
1128 int len = 0;
1129 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "") != 0)
1130 return 1;
1131 snprintf(kw, sizeof(kw), conf_definition(ex_filetype()), cw);
1132 if (lbuf_search(xb, kw, +1, &r, &o, &len) != 0) {
1133 snprintf(vi_msg, sizeof(vi_msg), "not found <%s>", kw);
1134 return 1;
1136 ln = lbuf_get(xb, r);
1137 if ((s = strstr(ln, cw)) != NULL)
1138 o = s - ln;
1139 vi_marksave();
1140 if (newwin) {
1141 vi_wonly();
1142 vi_wsplit();
1144 xrow = r;
1145 xoff = o;
1146 return 0;
1149 static int vi_openpath(char *path, int ln, int newwin)
1151 char ex[256];
1152 char *sep = strchr(path, ':');
1153 if (sep)
1154 *sep = '\0';
1155 if (access(path, R_OK) != 0) {
1156 snprintf(vi_msg, sizeof(vi_msg), "cannot open <%s>", path);
1157 return 1;
1159 if (newwin)
1160 vi_wsplit();
1161 snprintf(ex, sizeof(ex), "e %s", path);
1162 if (ex_command(ex))
1163 return 1;
1164 if (ln && sep && isdigit((unsigned char) sep[1])) {
1165 char *col = strchr(sep + 1, ':');
1166 int lnum = atoi(sep + 1);
1167 vi_marksave();
1168 xrow = MIN(MAX(0, lnum - 1), lbuf_len(xb) - 1);
1169 if (col && isdigit((unsigned char) col[1]))
1170 xoff = ren_noeol(lbuf_get(xb, xrow), atoi(col + 1) - 1);
1172 return 0;
1175 static int vc_openpath(int ln, int newwin)
1177 char cw[250];
1178 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "-/.:") != 0)
1179 return 1;
1180 return vi_openpath(cw, ln, newwin);
1183 static int vc_tag(int newwin)
1185 char cw[120], ex[128];
1186 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "") != 0)
1187 return 1;
1188 snprintf(ex, sizeof(ex), "ta %s", cw);
1189 vi_marksave();
1190 if (ex_command(ex) != 0)
1191 return 1;
1192 if (newwin) {
1193 ex_command("po");
1194 vi_wonly();
1195 vi_wsplit();
1196 ex_command(ex);
1198 return 0;
1201 static char rep_cmd[4096]; /* the last command */
1202 static int rep_len;
1204 static void vc_repeat(void)
1206 int i;
1207 for (i = 0; i < MAX(1, vi_arg1); i++)
1208 term_push(rep_cmd, rep_len);
1211 static void vc_execute(void)
1213 static int reg = -1;
1214 int lnmode;
1215 int c = vi_read();
1216 char *buf = NULL;
1217 int i;
1218 if (TK_INT(c))
1219 return;
1220 if (c == '@')
1221 c = reg;
1222 reg = c;
1223 if (reg >= 0)
1224 buf = reg_get(reg, &lnmode);
1225 if (buf != NULL) {
1226 for (i = 0; i < MAX(1, vi_arg1); i++) {
1227 term_push(buf, strlen(buf));
1228 term_push("\n", lnmode);
1233 static int vc_ecmd(int c, int newwin)
1235 char cmd[256];
1236 char *out;
1237 snprintf(cmd, sizeof(cmd), "%s %c %s %d %d",
1238 conf_ecmd(), c, ex_path(), xrow + 1, xoff + 1);
1239 if ((out = cmd_pipe(cmd, NULL, 2)) == NULL) {
1240 snprintf(vi_msg, sizeof(vi_msg), "command failed");
1241 return 1;
1243 if (!strchr(out, '\n')) {
1244 snprintf(vi_msg, sizeof(vi_msg), "no output");
1245 free(out);
1246 return 1;
1248 if (newwin) {
1249 vi_wonly();
1250 vi_wsplit();
1252 ex_command(out);
1253 free(out);
1254 return 0;
1257 static void sigwinch(int signo)
1259 vi_back(TK_CTL('l'));
1260 vi_back(TK_CTL('c'));
1263 static void vi(void)
1265 int xcol;
1266 int mark;
1267 char *ln;
1268 int kmap = 0;
1269 signal(SIGWINCH, sigwinch);
1270 vi_switch(0);
1271 xtop = MAX(0, xrow - xrows / 2);
1272 xoff = 0;
1273 xcol = vi_off2col(xb, xrow, xoff);
1274 vi_drawagain(xcol, 0);
1275 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1276 while (!xquit) {
1277 int mod = 0; /* redrawn the screen (1: window, 2: current line, 4: other windows) */
1278 int nrow = xrow;
1279 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1280 int otop = xtop;
1281 int oleft = xleft;
1282 int orow = xrow;
1283 char *opath = ex_path(); /* do not dereference; to detect buffer changes */
1284 int mv, n, ru;
1285 term_cmd(&n);
1286 vi_arg2 = 0;
1287 vi_ybuf = vi_yankbuf();
1288 vi_arg1 = vi_prefix();
1289 if (!vi_ybuf)
1290 vi_ybuf = vi_yankbuf();
1291 mv = vi_motion(&nrow, &noff);
1292 if (mv > 0) {
1293 if (strchr("\'`GHML/?{}[]nN", mv) || (mv == '%' && noff < 0))
1294 vi_marksave();
1295 xrow = nrow;
1296 if (noff < 0 && !strchr("jk", mv))
1297 noff = lbuf_indents(xb, xrow);
1298 if (strchr("jk", mv))
1299 noff = vi_col2off(xb, xrow, xcol);
1300 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1301 if (!strchr("|jk", mv))
1302 xcol = vi_off2col(xb, xrow, xoff);
1303 if (mv == '|')
1304 xcol = vi_pcol;
1305 } else if (mv == 0) {
1306 char *cmd;
1307 int c = vi_read();
1308 int k = 0;
1309 if (c <= 0)
1310 continue;
1311 lbuf_mark(xb, '*', xrow, xoff);
1312 switch (c) {
1313 case TK_CTL('b'):
1314 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1315 break;
1316 xoff = lbuf_indents(xb, xrow);
1317 mod = 1;
1318 break;
1319 case TK_CTL('f'):
1320 if (vi_scrollforward(MAX(1, vi_arg1) * (xrows - 1)))
1321 break;
1322 xoff = lbuf_indents(xb, xrow);
1323 mod = 1;
1324 break;
1325 case TK_CTL('e'):
1326 if (vi_scrollforward(MAX(1, vi_arg1)))
1327 break;
1328 xoff = vi_col2off(xb, xrow, xcol);
1329 break;
1330 case TK_CTL('y'):
1331 if (vi_scrollbackward(MAX(1, vi_arg1)))
1332 break;
1333 xoff = vi_col2off(xb, xrow, xcol);
1334 break;
1335 case TK_CTL('u'):
1336 if (xrow == 0)
1337 break;
1338 if (vi_arg1)
1339 vi_scroll = vi_arg1;
1340 n = vi_scroll ? vi_scroll : xrows / 2;
1341 xrow = MAX(0, xrow - n);
1342 if (xtop > 0)
1343 xtop = MAX(0, xtop - n);
1344 xoff = lbuf_indents(xb, xrow);
1345 mod = 1;
1346 break;
1347 case TK_CTL('d'):
1348 if (xrow == lbuf_len(xb) - 1)
1349 break;
1350 if (vi_arg1)
1351 vi_scroll = vi_arg1;
1352 n = vi_scroll ? vi_scroll : xrows / 2;
1353 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1354 if (xtop < lbuf_len(xb) - xrows)
1355 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1356 xoff = lbuf_indents(xb, xrow);
1357 mod = 1;
1358 break;
1359 case TK_CTL('z'):
1360 term_pos(xrows, 0);
1361 term_suspend();
1362 mod = 5;
1363 break;
1364 case 'u':
1365 if (!lbuf_undo(xb)) {
1366 lbuf_jump(xb, '*', &xrow, &xoff);
1367 mod = 1;
1368 } else {
1369 snprintf(vi_msg, sizeof(vi_msg), "undo failed");
1371 break;
1372 case TK_CTL('r'):
1373 if (!lbuf_redo(xb)) {
1374 lbuf_jump(xb, '*', &xrow, &xoff);
1375 mod = 1;
1376 } else {
1377 snprintf(vi_msg, sizeof(vi_msg), "redo failed");
1379 break;
1380 case TK_CTL('g'):
1381 vc_status();
1382 break;
1383 case TK_CTL('^'):
1384 ex_command("e #");
1385 mod = 1;
1386 break;
1387 case TK_CTL(']'):
1388 if (!vc_tag(0))
1389 mod = 1;
1390 break;
1391 case TK_CTL('t'):
1392 if (!ex_command("pop")) {
1393 vi_marksave();
1394 mod = 1;
1396 break;
1397 case TK_CTL('w'):
1398 k = vi_read();
1399 if (k == 's')
1400 if (!vi_wsplit())
1401 mod = 5;
1402 if (k == 'j' || k == 'k') {
1403 if (w_cnt > 1) {
1404 vi_switch(1 - w_cur);
1405 mod = 5;
1408 if (k == 'o')
1409 if (!vi_wonly())
1410 mod = 1;
1411 if (k == 'c')
1412 if (!vi_wclose())
1413 mod = 1;
1414 if (k == 'x')
1415 if (!vi_wswap())
1416 mod = 5;
1417 if (k == TK_CTL(']') || k == ']')
1418 if (!vc_tag(1))
1419 mod = 5;
1420 if (k == 'g') {
1421 int j = vi_read();
1422 if (j == 'f' || j == 'l')
1423 if (!vc_openpath(j == 'l', 1))
1424 mod = 5;
1425 if (j == 'd')
1426 if (!vc_definition(1))
1427 mod = 5;
1429 if (k == 'q') {
1430 int j = vi_read();
1431 if (isalpha(j) && !vc_ecmd(j, 1))
1432 mod = 5;
1434 break;
1435 case ':':
1436 ln = vi_prompt(":", &kmap);
1437 if (ln && ln[0]) {
1438 if (ln[0] != ':') {
1439 char *ln2 = uc_cat(":", ln);
1440 free(ln);
1441 ln = ln2;
1443 term_record();
1444 if (!ex_command(ln))
1445 mod = 5;
1446 term_commit();
1447 reg_put(':', ln, 1);
1449 free(ln);
1450 if (xquit)
1451 continue;
1452 break;
1453 case 'c':
1454 case 'd':
1455 case 'y':
1456 case '!':
1457 case '>':
1458 case '<':
1459 if (!vc_motion(c))
1460 mod = 1;
1461 break;
1462 case 'i':
1463 case 'I':
1464 case 'a':
1465 case 'A':
1466 case 'o':
1467 case 'O':
1468 if (!vc_insert(c))
1469 mod = 1;
1470 break;
1471 case 'J':
1472 if (!vc_join())
1473 mod = 1;
1474 break;
1475 case TK_CTL('l'):
1476 term_done();
1477 term_init();
1478 mod = 5;
1479 break;
1480 case 'm':
1481 if ((mark = vi_read()) > 0 && islower(mark))
1482 lbuf_mark(xb, mark, xrow, xoff);
1483 break;
1484 case 'p':
1485 case 'P':
1486 if (!vc_put(c))
1487 mod = 1;
1488 break;
1489 case 'z':
1490 k = vi_read();
1491 switch (k) {
1492 case '\n':
1493 xtop = vi_arg1 ? vi_arg1 : xrow;
1494 mod = 1;
1495 break;
1496 case '.':
1497 n = vi_arg1 ? vi_arg1 : xrow;
1498 xtop = MAX(0, n - xrows / 2);
1499 mod = 1;
1500 break;
1501 case '-':
1502 n = vi_arg1 ? vi_arg1 : xrow;
1503 xtop = MAX(0, n - xrows + 1);
1504 mod = 1;
1505 break;
1506 case 'l':
1507 case 'r':
1508 xtd = k == 'r' ? -1 : +1;
1509 mod = 1;
1510 break;
1511 case 'L':
1512 case 'R':
1513 xtd = k == 'R' ? -2 : +2;
1514 mod = 1;
1515 break;
1516 case 'e':
1517 case 'f':
1518 xkmap = k == 'e' ? 0 : xkmap_alt;
1519 break;
1520 case 'j':
1521 case 'k':
1522 if (!ex_command(k == 'j' ? "b +" : "b -"))
1523 mod = 1;
1524 break;
1525 case 'J':
1526 case 'K':
1527 if (!ex_command(k == 'J' ? "next" : "prev"))
1528 mod = 1;
1529 break;
1530 case 'D':
1531 if (!ex_command("b !"))
1532 mod = 1;
1533 break;
1535 break;
1536 case 'g':
1537 k = vi_read();
1538 if (k == '~' || k == 'u' || k == 'U')
1539 if (!vc_motion(k))
1540 mod = 2;
1541 if (k == 'a')
1542 vc_charinfo();
1543 if (k == 'd')
1544 if (!vc_definition(0))
1545 mod = 1;
1546 if (k == 'f' || k == 'l')
1547 if (!vc_openpath(k == 'l', 0))
1548 mod = 1;
1549 break;
1550 case 'x':
1551 vi_back(' ');
1552 if (!vc_motion('d'))
1553 mod = 2;
1554 break;
1555 case 'X':
1556 vi_back(TK_CTL('h'));
1557 if (!vc_motion('d'))
1558 mod = 2;
1559 break;
1560 case 'C':
1561 vi_back('$');
1562 if (!vc_motion('c'))
1563 mod = 1;
1564 break;
1565 case 'D':
1566 vi_back('$');
1567 if (!vc_motion('d'))
1568 mod = 2;
1569 break;
1570 case 'q':
1571 k = vi_read();
1572 if (isalpha(k) && !vc_ecmd(k, 0))
1573 mod = 5;
1574 break;
1575 case 'r':
1576 if (!vc_replace())
1577 mod = 2;
1578 break;
1579 case 's':
1580 vi_back(' ');
1581 if (!vc_motion('c'))
1582 mod = 1;
1583 break;
1584 case 'S':
1585 vi_back('c');
1586 if (!vc_motion('c'))
1587 mod = 1;
1588 break;
1589 case 'Y':
1590 vi_back('y');
1591 vc_motion('y');
1592 break;
1593 case 'Z':
1594 k = vi_read();
1595 if (k == 'Z')
1596 if (!ex_command("x"))
1597 mod = 1;
1598 break;
1599 case '~':
1600 vi_back(' ');
1601 if (!vc_motion('~'))
1602 mod = 2;
1603 break;
1604 case '.':
1605 vc_repeat();
1606 break;
1607 case '@':
1608 vc_execute();
1609 break;
1610 default:
1611 continue;
1613 cmd = term_cmd(&n);
1614 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1615 (c == 'g' && strchr("uU~", k))) {
1616 if (n + 1 < sizeof(rep_cmd)) {
1617 memcpy(rep_cmd, cmd, n);
1618 rep_len = n;
1619 rep_cmd[n] = '\0';
1620 reg_put('.', rep_cmd, 0);
1624 vi_wfix();
1625 if (mod)
1626 xcol = vi_off2col(xb, xrow, xoff);
1627 if (xcol >= xleft + xcols)
1628 xleft = xcol - xcols / 2;
1629 if (xcol < xleft)
1630 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1631 vi_wait();
1632 ru = (xru & 1) || ((xru & 2) && w_cnt > 1) || ((xru & 4) && opath != ex_path());
1633 if (mod & 4 && w_cnt > 1) {
1634 char msg[sizeof(vi_msg)];
1635 int id = w_cur;
1636 w_tmp = 1;
1637 vi_switch(1 - id);
1638 vi_wfix();
1639 strcpy(msg, vi_msg);
1640 if (ru)
1641 vc_status();
1642 vi_drawagain(vi_off2col(xb, xrow, xoff), 0);
1643 strcpy(vi_msg, msg);
1644 w_tmp = 0;
1645 vi_switch(id);
1647 if (ru && !vi_msg[0])
1648 vc_status();
1649 if (mod || xleft != oleft) {
1650 vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
1651 } else {
1652 if (xtop != otop)
1653 vi_drawupdate(xcol, otop);
1654 if (xhll && xrow != orow && orow >= xtop && orow < xtop + xrows)
1655 vi_drawrow(orow);
1656 if (xhll && xrow != orow)
1657 vi_drawrow(xrow);
1658 if (vi_msg[0])
1659 vi_drawmsg();
1661 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1662 ren_cursor(lbuf_get(xb, xrow), xcol)));
1663 lbuf_modified(xb);
1667 int main(int argc, char *argv[])
1669 int i;
1670 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1671 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1672 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1673 if (argv[i][1] == 's')
1674 xled = 0;
1675 if (argv[i][1] == 'e')
1676 xvis = 0;
1677 if (argv[i][1] == 'v')
1678 xvis = 1;
1679 if (argv[i][1] == 'h') {
1680 printf("usage: %s [options] [file...]\n\n", argv[0]);
1681 printf("options:\n");
1682 printf(" -v start in vi mode\n");
1683 printf(" -e start in ex mode\n");
1684 printf(" -s silent mode (for ex mode only)\n");
1685 return 0;
1688 dir_init();
1689 syn_init();
1690 tag_init(getenv("TAGPATH") ? getenv("TAGPATH") : "tags");
1691 if (!ex_init(argv + i)) {
1692 if (xled || xvis)
1693 term_init();
1694 if (xvis)
1695 vi();
1696 else
1697 ex();
1698 if (xled || xvis)
1699 term_done();
1700 ex_done();
1702 free(w_path);
1703 reg_done();
1704 syn_done();
1705 dir_done();
1706 tag_done();
1707 return 0;