stag: allow multiple tag patterns for each extension
[neatvi.git] / vi.c
blob2dfac8cd187d747b9429b9928247a6b437cc8fca
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 ? "---" : "___", NULL));
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, char *hist)
220 char *r, *s;
221 term_pos(xrows, led_pos(msg, 0));
222 term_kill();
223 s = led_prompt(msg, "", kmap, xhl ? "---" : "___", xhist != 0 ? hist : NULL);
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 (xvis)
237 term_pos(xrows - 1, 0);
238 if (xled) {
239 char *s = led_prompt(msg, "", &xkmap, xhl ? "---" : "___", NULL);
240 if (s)
241 term_chr('\n');
242 return s;
244 sb = sbuf_make();
245 while ((c = getchar()) != EOF && c != '\n')
246 sbuf_chr(sb, c);
247 if (c == EOF) {
248 sbuf_free(sb);
249 return NULL;
251 return sbuf_done(sb);
254 /* show an ex message */
255 void ex_show(char *msg)
257 if (xvis) {
258 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
259 } else if (xled) {
260 led_print(msg, -1, xhl ? "---" : "___");
261 term_chr('\n');
262 } else {
263 printf("%s", msg);
267 /* print an ex output line */
268 void ex_print(char *line)
270 if (xvis) {
271 if (line && vi_printed == 0)
272 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
273 if (line && vi_printed == 1)
274 led_print(vi_msg, xrows - 1, xhl ? "-ex" : "");
275 if (vi_printed)
276 term_chr('\n');
277 if (line && vi_printed)
278 led_print(line, xrows - 1, xhl ? "-ex" : "");
279 vi_printed += line != NULL ? 1 : -1000;
280 } else {
281 if (line)
282 ex_show(line);
286 static char *reg_getln(int h)
288 return reg_get(0x80 | h, NULL);
291 static void reg_putln(int h, char *s)
293 char *old = reg_get(0x80 | h, NULL);
294 struct sbuf *sb;
295 int i = 0;
296 if (xhist == 0 || s == NULL || s[0] == '\0' || s[0] == '\n')
297 return;
298 /* ignore duplicate lines */
299 if (old != NULL) {
300 while (s[i] && old[i] == s[i])
301 i++;
302 if (!s[i] && old[i] == '\n')
303 return;
305 /* omit old lines */
306 if (xhist > 0 && old != NULL) {
307 char *end = old;
308 for (i = 1; end != NULL && i < xhist; i++)
309 end = strchr(end == old ? end : end + 1, '\n');
310 if (end != NULL)
311 end[1] = '\0';
313 /* add the new line */
314 sb = sbuf_make();
315 sbuf_str(sb, s);
316 if (strchr(s, '\n') == NULL)
317 sbuf_chr(sb, '\n');
318 if (old != NULL)
319 sbuf_str(sb, old);
320 reg_put(0x80 | h, sbuf_buf(sb), 1);
321 sbuf_free(sb);
324 static int vi_yankbuf(void)
326 int c = vi_read();
327 if (c == '"')
328 return (c = vi_read()) == '\\' ? 0x80 | vi_read() : c;
329 vi_back(c);
330 return 0;
333 static int vi_prefix(void)
335 int n = 0;
336 int c = vi_read();
337 if ((c >= '1' && c <= '9')) {
338 while (isdigit(c)) {
339 n = n * 10 + c - '0';
340 c = vi_read();
343 vi_back(c);
344 return n;
347 static int vi_col2off(struct lbuf *lb, int row, int col)
349 char *ln = lbuf_get(lb, row);
350 return ln ? ren_off(ln, col) : 0;
353 static int vi_off2col(struct lbuf *lb, int row, int off)
355 char *ln = lbuf_get(lb, row);
356 return ln ? ren_pos(ln, off) : 0;
359 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
361 int o = *off + dir;
362 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
363 return 1;
364 *off = o;
365 return 0;
368 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
370 char *ln = lbuf_get(lb, *row);
371 int col = ln ? ren_pos(ln, *off) : 0;
372 int o = ln ? ren_next(ln, col, dir) : -1;
373 if (o < 0)
374 return -1;
375 *off = ren_off(ln, o);
376 return 0;
379 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
381 if (cs != vi_charlast)
382 strcpy(vi_charlast, cs);
383 vi_charcmd = cmd;
384 return lbuf_findchar(lb, cs, cmd, n, row, off);
387 static int vi_search(int cmd, int cnt, int *row, int *off)
389 char *kwd;
390 int r = *row;
391 int o = *off;
392 char *failed = NULL;
393 int len = 0;
394 int i, dir;
395 if (cmd == '/' || cmd == '?') {
396 char sign[4] = {cmd};
397 struct sbuf *sb;
398 char *kw = vi_prompt(sign, &xkmap, reg_getln('/'));
399 char *re;
400 if (!kw)
401 return 1;
402 sb = sbuf_make();
403 sbuf_chr(sb, cmd);
404 sbuf_str(sb, kw);
405 free(kw);
406 kw = sbuf_buf(sb);
407 if ((re = re_read(&kw))) {
408 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
409 if (re[0]) {
410 reg_putln('/', re);
411 reg_put('/', re, 0);
413 while (isspace(*kw))
414 kw++;
415 vi_soset = !!kw[0];
416 vi_so = atoi(kw);
417 free(re);
419 sbuf_free(sb);
421 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
422 return 1;
423 dir = cmd == 'N' ? -dir : dir;
424 o = *off;
425 for (i = 0; i < cnt; i++) {
426 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
427 failed = " not found";
428 break;
430 if (i + 1 < cnt && cmd == '/')
431 o += len;
433 if (!failed) {
434 *row = r;
435 *off = o;
436 if (vi_soset) {
437 *off = -1;
438 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
439 failed = " bad offset";
440 else
441 *row += vi_so;
444 if (failed != NULL)
445 snprintf(vi_msg, sizeof(vi_msg), "/%s/%s", kwd, failed ? failed : "");
446 return failed != NULL;
449 /* read a line motion */
450 static int vi_motionln(int *row, int cmd)
452 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
453 int c = vi_read();
454 int mark, mark_row, mark_off;
455 switch (c) {
456 case '\n':
457 case '+':
458 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
459 break;
460 case '-':
461 *row = MAX(*row - cnt, 0);
462 break;
463 case '_':
464 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
465 break;
466 case '\'':
467 if ((mark = vi_read()) <= 0)
468 return -1;
469 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
470 return -1;
471 *row = mark_row;
472 break;
473 case 'j':
474 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
475 break;
476 case 'k':
477 *row = MAX(*row - cnt, 0);
478 break;
479 case 'G':
480 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
481 break;
482 case 'H':
483 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
484 break;
485 case 'L':
486 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
487 break;
488 case 'M':
489 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
490 break;
491 default:
492 if (c == cmd) {
493 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
494 break;
496 if (c == '%' && (vi_arg1 || vi_arg2)) {
497 if (cnt > 100)
498 return -1;
499 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
500 break;
502 vi_back(c);
503 return 0;
505 if (*row < 0)
506 *row = 0;
507 return c;
510 static int vi_curword(struct lbuf *lb, char *dst, int len, int row, int off, char *ext)
512 char *ln = lbuf_get(lb, row);
513 char *beg, *end;
514 if (!ln)
515 return 1;
516 beg = uc_chr(ln, ren_noeol(ln, off));
517 end = beg;
518 while (*end && (uc_kind(end) == 1 ||
519 strchr(ext, (unsigned char) end[0]) != NULL))
520 end = uc_next(end);
521 while (beg > ln && (uc_kind(uc_beg(ln, beg - 1)) == 1 ||
522 strchr(ext, (unsigned char) beg[-1]) != NULL))
523 beg = uc_beg(ln, beg - 1);
524 if (beg >= end)
525 return 1;
526 len = len - 1 < end - beg ? len - 1 : end - beg;
527 dst[len] = '\0';
528 memcpy(dst, beg, len);
529 return 0;
532 /* read a motion */
533 static int vi_motion(int *row, int *off)
535 char cw[120], kw[128];
536 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
537 char *ln = lbuf_get(xb, *row);
538 int dir = dir_context(ln ? ln : "");
539 int mark, mark_row, mark_off;
540 char *cs;
541 int mv;
542 int i;
543 if ((mv = vi_motionln(row, 0))) {
544 *off = -1;
545 return mv;
547 mv = vi_read();
548 switch (mv) {
549 case 'f':
550 if (!(cs = vi_char()))
551 return -1;
552 if (vi_findchar(xb, cs, mv, cnt, row, off))
553 return -1;
554 break;
555 case 'F':
556 if (!(cs = vi_char()))
557 return -1;
558 if (vi_findchar(xb, cs, mv, cnt, row, off))
559 return -1;
560 break;
561 case ';':
562 if (!vi_charlast[0])
563 return -1;
564 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
565 return -1;
566 break;
567 case ',':
568 if (!vi_charlast[0])
569 return -1;
570 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
571 return -1;
572 break;
573 case 'h':
574 for (i = 0; i < cnt; i++)
575 if (vi_nextcol(xb, -1 * dir, row, off))
576 break;
577 break;
578 case 'l':
579 for (i = 0; i < cnt; i++)
580 if (vi_nextcol(xb, +1 * dir, row, off))
581 break;
582 break;
583 case 't':
584 if (!(cs = vi_char()))
585 return -1;
586 if (vi_findchar(xb, cs, mv, cnt, row, off))
587 return -1;
588 break;
589 case 'T':
590 if (!(cs = vi_char()))
591 return -1;
592 if (vi_findchar(xb, cs, mv, cnt, row, off))
593 return -1;
594 break;
595 case 'B':
596 for (i = 0; i < cnt; i++)
597 if (lbuf_wordend(xb, 1, -1, row, off))
598 break;
599 break;
600 case 'E':
601 for (i = 0; i < cnt; i++)
602 if (lbuf_wordend(xb, 1, +1, row, off))
603 break;
604 break;
605 case 'W':
606 for (i = 0; i < cnt; i++)
607 if (lbuf_wordbeg(xb, 1, +1, row, off))
608 break;
609 break;
610 case 'b':
611 for (i = 0; i < cnt; i++)
612 if (lbuf_wordend(xb, 0, -1, row, off))
613 break;
614 break;
615 case 'e':
616 for (i = 0; i < cnt; i++)
617 if (lbuf_wordend(xb, 0, +1, row, off))
618 break;
619 break;
620 case 'w':
621 for (i = 0; i < cnt; i++)
622 if (lbuf_wordbeg(xb, 0, +1, row, off))
623 break;
624 break;
625 case '{':
626 for (i = 0; i < cnt; i++)
627 if (lbuf_paragraphbeg(xb, -1, row, off))
628 break;
629 break;
630 case '}':
631 for (i = 0; i < cnt; i++)
632 if (lbuf_paragraphbeg(xb, +1, row, off))
633 break;
634 break;
635 case '[':
636 if (vi_read() != '[')
637 return -1;
638 for (i = 0; i < cnt; i++)
639 if (lbuf_sectionbeg(xb, -1, conf_section(ex_filetype()), row, off))
640 break;
641 break;
642 case ']':
643 if (vi_read() != ']')
644 return -1;
645 for (i = 0; i < cnt; i++)
646 if (lbuf_sectionbeg(xb, +1, conf_section(ex_filetype()), row, off))
647 break;
648 break;
649 case '0':
650 *off = 0;
651 break;
652 case '^':
653 *off = lbuf_indents(xb, *row);
654 break;
655 case '$':
656 *off = lbuf_eol(xb, *row);
657 break;
658 case '|':
659 *off = vi_col2off(xb, *row, cnt - 1);
660 vi_pcol = cnt - 1;
661 break;
662 case '/':
663 if (vi_search(mv, cnt, row, off))
664 return -1;
665 break;
666 case '?':
667 if (vi_search(mv, cnt, row, off))
668 return -1;
669 break;
670 case 'n':
671 if (vi_search(mv, cnt, row, off))
672 return -1;
673 break;
674 case 'N':
675 if (vi_search(mv, cnt, row, off))
676 return -1;
677 break;
678 case TK_CTL('a'):
679 if (vi_curword(xb, cw, sizeof(cw), *row, *off, "") != 0)
680 return -1;
681 snprintf(kw, sizeof(kw), "\\<%s\\>", cw);
682 ex_kwdset(kw, +1);
683 vi_soset = 0;
684 if (vi_search('n', cnt, row, off))
685 return -1;
686 break;
687 case ' ':
688 for (i = 0; i < cnt; i++)
689 if (vi_nextoff(xb, +1, row, off))
690 break;
691 break;
692 case 127:
693 case TK_CTL('h'):
694 for (i = 0; i < cnt; i++)
695 if (vi_nextoff(xb, -1, row, off))
696 break;
697 break;
698 case '`':
699 if ((mark = vi_read()) <= 0)
700 return -1;
701 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
702 return -1;
703 *row = mark_row;
704 *off = mark_off;
705 break;
706 case '%':
707 if (lbuf_pair(xb, row, off))
708 return -1;
709 break;
710 default:
711 vi_back(mv);
712 return 0;
714 return mv;
717 static void swap(int *a, int *b)
719 int t = *a;
720 *a = *b;
721 *b = t;
724 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
726 struct sbuf *sb;
727 char *s1, *s2, *s3;
728 if (r1 == r2)
729 return uc_sub(lbuf_get(lb, r1), o1, o2);
730 sb = sbuf_make();
731 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
732 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
733 s2 = lbuf_cp(lb, r1 + 1, r2);
734 sbuf_str(sb, s1);
735 sbuf_str(sb, s2);
736 sbuf_str(sb, s3);
737 free(s1);
738 free(s2);
739 free(s3);
740 return sbuf_done(sb);
743 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
745 char *region;
746 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
747 reg_put(vi_ybuf, region, lnmode);
748 free(region);
749 xrow = r1;
750 xoff = lnmode ? xoff : o1;
753 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
755 char *pref, *post;
756 char *region;
757 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
758 reg_put(vi_ybuf, region, lnmode);
759 free(region);
760 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
761 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
762 if (!lnmode) {
763 char *line = uc_cat(pref, post);
764 lbuf_edit(xb, line, r1, r2 + 1);
765 free(line);
766 } else {
767 lbuf_edit(xb, NULL, r1, r2 + 1);
769 xrow = r1;
770 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
771 free(pref);
772 free(post);
775 static int linecount(char *s)
777 int n;
778 for (n = 0; s; n++)
779 if ((s = strchr(s, '\n')))
780 s++;
781 return n;
784 static int charcount(char *text, char *post)
786 int tlen = strlen(text);
787 int plen = strlen(post);
788 char *nl = text;
789 int i;
790 if (tlen < plen)
791 return 0;
792 for (i = 0; i < tlen - plen; i++)
793 if (text[i] == '\n')
794 nl = text + i + 1;
795 return uc_slen(nl) - uc_slen(post);
798 static char *vi_input(char *pref, char *post, int *row, int *off)
800 char *rep = led_input(pref, post, &xkmap, xhl ? ex_filetype() : "");
801 if (!rep)
802 return NULL;
803 *row = linecount(rep) - 1;
804 *off = charcount(rep, post) - 1;
805 if (*off < 0)
806 *off = 0;
807 return rep;
810 static char *vi_indents(char *ln)
812 struct sbuf *sb = sbuf_make();
813 while (xai && ln && (*ln == ' ' || *ln == '\t'))
814 sbuf_chr(sb, *ln++);
815 return sbuf_done(sb);
818 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
820 char *region;
821 int row, off;
822 char *rep;
823 char *pref, *post;
824 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
825 reg_put(vi_ybuf, region, lnmode);
826 free(region);
827 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
828 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
829 vi_drawrm(r1, r2, 0);
830 rep = vi_input(pref, post, &row, &off);
831 if (rep) {
832 lbuf_edit(xb, rep, r1, r2 + 1);
833 xrow = r1 + row - 1;
834 xoff = off;
835 free(rep);
837 free(pref);
838 free(post);
841 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
843 char *pref, *post;
844 char *region, *s;
845 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
846 s = region;
847 while (*s) {
848 int c = (unsigned char) s[0];
849 if (c <= 0x7f) {
850 if (cmd == 'u')
851 s[0] = tolower(c);
852 if (cmd == 'U')
853 s[0] = toupper(c);
854 if (cmd == '~')
855 s[0] = islower(c) ? toupper(c) : tolower(c);
857 s = uc_next(s);
859 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
860 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
861 if (!lnmode) {
862 struct sbuf *sb = sbuf_make();
863 sbuf_str(sb, pref);
864 sbuf_str(sb, region);
865 sbuf_str(sb, post);
866 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
867 sbuf_free(sb);
868 } else {
869 lbuf_edit(xb, region, r1, r2 + 1);
871 xrow = r2;
872 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
873 free(region);
874 free(pref);
875 free(post);
878 static void vi_pipe(int r1, int r2)
880 char *text;
881 char *rep;
882 int kmap = 0;
883 char *cmd = vi_prompt("!", &kmap, reg_getln('!'));
884 if (!cmd)
885 return;
886 reg_put('!', cmd, 1);
887 reg_putln('!', cmd);
888 text = lbuf_cp(xb, r1, r2 + 1);
889 rep = cmd_pipe(cmd, text, 1);
890 if (rep)
891 lbuf_edit(xb, rep, r1, r2 + 1);
892 free(cmd);
893 free(text);
894 free(rep);
897 static void vi_shift(int r1, int r2, int dir)
899 struct sbuf *sb;
900 char *ln;
901 int i;
902 for (i = r1; i <= r2; i++) {
903 if (!(ln = lbuf_get(xb, i)))
904 continue;
905 sb = sbuf_make();
906 if (dir > 0) {
907 if (ln[0] != '\n')
908 sbuf_chr(sb, '\t');
909 } else {
910 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
912 sbuf_str(sb, ln);
913 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
914 sbuf_free(sb);
916 xrow = r1;
917 xoff = lbuf_indents(xb, xrow);
920 static int vc_motion(int cmd)
922 int r1 = xrow, r2 = xrow; /* region rows */
923 int o1 = xoff, o2 = xoff; /* visual region columns */
924 int lnmode = 0; /* line-based region */
925 int mv;
926 vi_arg2 = vi_prefix();
927 if (vi_arg2 < 0)
928 return 1;
929 o1 = ren_noeol(lbuf_get(xb, r1), o1);
930 o2 = o1;
931 if ((mv = vi_motionln(&r2, cmd))) {
932 o2 = -1;
933 } else if (!(mv = vi_motion(&r2, &o2))) {
934 vi_read();
935 return 1;
937 if (mv < 0)
938 return 1;
939 lnmode = o2 < 0;
940 if (lnmode) {
941 o1 = 0;
942 o2 = lbuf_eol(xb, r2);
944 if (r1 > r2) {
945 swap(&r1, &r2);
946 swap(&o1, &o2);
948 if (r1 == r2 && o1 > o2)
949 swap(&o1, &o2);
950 o1 = ren_noeol(lbuf_get(xb, r1), o1);
951 if (!lnmode && strchr("fFtTeE%", mv))
952 if (o2 < lbuf_eol(xb, r2))
953 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
954 if (cmd == 'y')
955 vi_yank(r1, o1, r2, o2, lnmode);
956 if (cmd == 'd')
957 vi_delete(r1, o1, r2, o2, lnmode);
958 if (cmd == 'c')
959 vi_change(r1, o1, r2, o2, lnmode);
960 if (cmd == '~' || cmd == 'u' || cmd == 'U')
961 vi_case(r1, o1, r2, o2, lnmode, cmd);
962 if (cmd == '>' || cmd == '<')
963 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
964 if (cmd == '!') {
965 if (mv == '{' || mv == '}')
966 if (lbuf_get(xb, r2) && lbuf_get(xb, r2)[0] == '\n' && r1 < r2)
967 r2--;
968 vi_pipe(r1, r2);
970 return 0;
973 static int vc_insert(int cmd)
975 char *pref, *post;
976 char *ln = lbuf_get(xb, xrow);
977 int row, off = 0;
978 char *rep;
979 if (cmd == 'I')
980 xoff = lbuf_indents(xb, xrow);
981 if (cmd == 'A')
982 xoff = lbuf_eol(xb, xrow);
983 xoff = ren_noeol(ln, xoff);
984 if (cmd == 'o')
985 xrow += 1;
986 if (cmd == 'i' || cmd == 'I')
987 off = xoff;
988 if (cmd == 'a' || cmd == 'A')
989 off = xoff + 1;
990 if (ln && ln[0] == '\n')
991 off = 0;
992 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
993 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
994 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
995 rep = vi_input(pref, post, &row, &off);
996 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
997 lbuf_edit(xb, "\n", 0, 0);
998 if (rep) {
999 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
1000 xrow += row - 1;
1001 xoff = off;
1002 free(rep);
1004 free(pref);
1005 free(post);
1006 return !rep;
1009 static int vc_put(int cmd)
1011 int cnt = MAX(1, vi_arg1);
1012 int lnmode;
1013 char *buf = reg_get(vi_ybuf, &lnmode);
1014 int i;
1015 if (!buf)
1016 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty");
1017 if (!buf || !buf[0])
1018 return 1;
1019 if (lnmode) {
1020 struct sbuf *sb = sbuf_make();
1021 for (i = 0; i < cnt; i++)
1022 sbuf_str(sb, buf);
1023 if (!lbuf_len(xb))
1024 lbuf_edit(xb, "\n", 0, 0);
1025 if (cmd == 'p')
1026 xrow++;
1027 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
1028 xoff = lbuf_indents(xb, xrow);
1029 sbuf_free(sb);
1030 } else {
1031 struct sbuf *sb = sbuf_make();
1032 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
1033 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
1034 char *s = uc_sub(ln, 0, off);
1035 sbuf_str(sb, s);
1036 free(s);
1037 for (i = 0; i < cnt; i++)
1038 sbuf_str(sb, buf);
1039 s = uc_sub(ln, off, -1);
1040 sbuf_str(sb, s);
1041 free(s);
1042 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1043 xoff = off + uc_slen(buf) * cnt - 1;
1044 sbuf_free(sb);
1046 return 0;
1049 static int join_spaces(char *prev, char *next)
1051 int prevlen = strlen(prev);
1052 if (!prev[0])
1053 return 0;
1054 if (prev[prevlen - 1] == ' ' || next[0] == ')')
1055 return 0;
1056 return prev[prevlen - 1] == '.' ? 2 : 1;
1059 static int vc_join(void)
1061 struct sbuf *sb;
1062 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
1063 int beg = xrow;
1064 int end = xrow + cnt;
1065 int off = 0;
1066 int i;
1067 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
1068 return 1;
1069 sb = sbuf_make();
1070 for (i = beg; i < end; i++) {
1071 char *ln = lbuf_get(xb, i);
1072 char *lnend = strchr(ln, '\n');
1073 int spaces;
1074 if (i > beg)
1075 while (ln[0] == ' ' || ln[0] == '\t')
1076 ln++;
1077 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
1078 off = uc_slen(sbuf_buf(sb));
1079 while (spaces--)
1080 sbuf_chr(sb, ' ');
1081 sbuf_mem(sb, ln, lnend - ln);
1083 sbuf_chr(sb, '\n');
1084 lbuf_edit(xb, sbuf_buf(sb), beg, end);
1085 xoff = off;
1086 sbuf_free(sb);
1087 return 0;
1090 static int vi_scrollforward(int cnt)
1092 if (xtop >= lbuf_len(xb) - 1)
1093 return 1;
1094 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
1095 xrow = MAX(xrow, xtop);
1096 return 0;
1099 static int vi_scrollbackward(int cnt)
1101 if (xtop == 0)
1102 return 1;
1103 xtop = MAX(0, xtop - cnt);
1104 xrow = MIN(xrow, xtop + xrows - 1);
1105 return 0;
1108 static void vc_status(void)
1110 int col = vi_off2col(xb, xrow, xoff);
1111 int win = w_tmp ? '-' : '=';
1112 snprintf(vi_msg, sizeof(vi_msg),
1113 "\"%s\"%c [%c%d] L%d C%d",
1114 ex_path()[0] ? ex_path() : "unnamed",
1115 lbuf_modified(xb) ? '*' : ' ',
1116 win, lbuf_len(xb), xrow + 1,
1117 ren_cursor(lbuf_get(xb, xrow), col) + 1);
1120 static void vc_charinfo(void)
1122 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
1123 if (c) {
1124 char cbuf[8] = "";
1125 memcpy(cbuf, c, uc_len(c));
1126 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x", cbuf, uc_code(c));
1130 static int vc_replace(void)
1132 int cnt = MAX(1, vi_arg1);
1133 char *cs = vi_char();
1134 char *ln = lbuf_get(xb, xrow);
1135 struct sbuf *sb;
1136 char *pref, *post;
1137 char *s;
1138 int off, i;
1139 if (!ln || !cs)
1140 return 1;
1141 off = ren_noeol(ln, xoff);
1142 s = uc_chr(ln, off);
1143 for (i = 0; s[0] != '\n' && i < cnt; i++)
1144 s = uc_next(s);
1145 if (i < cnt)
1146 return 1;
1147 pref = uc_sub(ln, 0, off);
1148 post = uc_sub(ln, off + cnt, -1);
1149 sb = sbuf_make();
1150 sbuf_str(sb, pref);
1151 for (i = 0; i < cnt; i++)
1152 sbuf_str(sb, cs);
1153 sbuf_str(sb, post);
1154 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1155 off += cnt - 1;
1156 xoff = off;
1157 sbuf_free(sb);
1158 free(pref);
1159 free(post);
1160 return 0;
1163 static void vi_marksave(void)
1165 lbuf_mark(xb, '\'', xrow, xoff);
1166 lbuf_mark(xb, '`', xrow, xoff);
1169 static int vc_definition(int newwin)
1171 char cw[256], kw[256];
1172 char *s, *ln;
1173 int r = 0, o = 0;
1174 int len = 0;
1175 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "") != 0)
1176 return 1;
1177 snprintf(kw, sizeof(kw), conf_definition(ex_filetype()), cw);
1178 if (lbuf_search(xb, kw, +1, &r, &o, &len) != 0) {
1179 snprintf(vi_msg, sizeof(vi_msg), "not found <%s>", kw);
1180 return 1;
1182 ln = lbuf_get(xb, r);
1183 if ((s = strstr(ln, cw)) != NULL)
1184 o = s - ln;
1185 vi_marksave();
1186 if (newwin) {
1187 vi_wonly();
1188 vi_wsplit();
1190 xrow = r;
1191 xoff = o;
1192 return 0;
1195 static int vi_openpath(char *path, int ln, int newwin)
1197 char ex[256];
1198 char *sep = strchr(path, ':');
1199 if (sep)
1200 *sep = '\0';
1201 if (access(path, R_OK) != 0) {
1202 snprintf(vi_msg, sizeof(vi_msg), "cannot open <%s>", path);
1203 return 1;
1205 if (newwin)
1206 vi_wsplit();
1207 snprintf(ex, sizeof(ex), "e %s", path);
1208 if (ex_command(ex))
1209 return 1;
1210 if (ln && sep && isdigit((unsigned char) sep[1])) {
1211 char *col = strchr(sep + 1, ':');
1212 int lnum = atoi(sep + 1);
1213 vi_marksave();
1214 xrow = MIN(MAX(0, lnum - 1), lbuf_len(xb) - 1);
1215 if (col && isdigit((unsigned char) col[1]))
1216 xoff = ren_noeol(lbuf_get(xb, xrow), atoi(col + 1) - 1);
1218 return 0;
1221 static int vc_openpath(int ln, int newwin)
1223 char cw[250];
1224 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "-/.:") != 0)
1225 return 1;
1226 return vi_openpath(cw, ln, newwin);
1229 static int vc_tag(int newwin)
1231 char cw[120], ex[128];
1232 if (vi_curword(xb, cw, sizeof(cw), xrow, xoff, "") != 0)
1233 return 1;
1234 snprintf(ex, sizeof(ex), "ta %s", cw);
1235 vi_marksave();
1236 if (ex_command(ex) != 0)
1237 return 1;
1238 if (newwin) {
1239 ex_command("po");
1240 vi_wonly();
1241 vi_wsplit();
1242 ex_command(ex);
1244 return 0;
1247 static char rep_cmd[4096]; /* the last command */
1248 static int rep_len;
1250 static void vc_repeat(void)
1252 int i;
1253 for (i = 0; i < MAX(1, vi_arg1); i++)
1254 term_push(rep_cmd, rep_len);
1257 static void vc_execute(void)
1259 static int reg = -1;
1260 int lnmode;
1261 int c = vi_read();
1262 char *buf = NULL;
1263 int i;
1264 if (TK_INT(c))
1265 return;
1266 if (c == '@')
1267 c = reg;
1268 reg = c;
1269 if (reg >= 0)
1270 buf = reg_get(reg, &lnmode);
1271 if (buf != NULL) {
1272 for (i = 0; i < MAX(1, vi_arg1); i++)
1273 term_push(buf, strlen(buf));
1277 static int vc_ecmd(int c, int newwin)
1279 char cmd[256];
1280 char *out;
1281 snprintf(cmd, sizeof(cmd), "%s %c %s %d %d",
1282 conf_ecmd(), c, ex_path(), xrow + 1, xoff + 1);
1283 if ((out = cmd_pipe(cmd, NULL, 2)) == NULL) {
1284 snprintf(vi_msg, sizeof(vi_msg), "command failed");
1285 return 1;
1287 if (!strchr(out, '\n')) {
1288 snprintf(vi_msg, sizeof(vi_msg), "no output");
1289 free(out);
1290 return 1;
1292 if (newwin) {
1293 vi_wonly();
1294 vi_wsplit();
1296 ex_command(out);
1297 free(out);
1298 return 0;
1301 static void sigwinch(int signo)
1303 vi_back(TK_CTL('l'));
1304 vi_back(TK_CTL('c'));
1307 static void vi(void)
1309 int xcol;
1310 int mark;
1311 char *ln;
1312 int kmap = 0;
1313 signal(SIGWINCH, sigwinch);
1314 vi_switch(0);
1315 xtop = MAX(0, xrow - xrows / 2);
1316 xoff = 0;
1317 xcol = vi_off2col(xb, xrow, xoff);
1318 vi_drawagain(xcol, 0);
1319 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1320 while (!xquit) {
1321 int mod = 0; /* redrawn the screen (1: window, 2: current line, 4: other windows) */
1322 int nrow = xrow;
1323 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1324 int otop = xtop;
1325 int oleft = xleft;
1326 int orow = xrow;
1327 char *opath = ex_path(); /* do not dereference; to detect buffer changes */
1328 int mv, n, ru;
1329 term_cmd(&n);
1330 vi_arg2 = 0;
1331 vi_ybuf = vi_yankbuf();
1332 vi_arg1 = vi_prefix();
1333 if (!vi_ybuf)
1334 vi_ybuf = vi_yankbuf();
1335 mv = vi_motion(&nrow, &noff);
1336 if (mv > 0) {
1337 if (strchr("\'`GHML/?{}[]nN", mv) || (mv == '%' && noff < 0))
1338 vi_marksave();
1339 xrow = nrow;
1340 if (noff < 0 && !strchr("jk", mv))
1341 noff = lbuf_indents(xb, xrow);
1342 if (strchr("jk", mv))
1343 noff = vi_col2off(xb, xrow, xcol);
1344 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1345 if (!strchr("|jk", mv))
1346 xcol = vi_off2col(xb, xrow, xoff);
1347 if (mv == '|')
1348 xcol = vi_pcol;
1349 } else if (mv == 0) {
1350 char *cmd;
1351 int c = vi_read();
1352 int k = 0;
1353 if (c <= 0)
1354 continue;
1355 lbuf_mark(xb, '*', xrow, xoff);
1356 switch (c) {
1357 case TK_CTL('b'):
1358 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1359 break;
1360 xoff = lbuf_indents(xb, xrow);
1361 mod = 1;
1362 break;
1363 case TK_CTL('f'):
1364 if (vi_scrollforward(MAX(1, vi_arg1) * (xrows - 1)))
1365 break;
1366 xoff = lbuf_indents(xb, xrow);
1367 mod = 1;
1368 break;
1369 case TK_CTL('e'):
1370 if (vi_scrollforward(MAX(1, vi_arg1)))
1371 break;
1372 xoff = vi_col2off(xb, xrow, xcol);
1373 break;
1374 case TK_CTL('y'):
1375 if (vi_scrollbackward(MAX(1, vi_arg1)))
1376 break;
1377 xoff = vi_col2off(xb, xrow, xcol);
1378 break;
1379 case TK_CTL('u'):
1380 if (xrow == 0)
1381 break;
1382 if (vi_arg1)
1383 vi_scroll = vi_arg1;
1384 n = vi_scroll ? vi_scroll : xrows / 2;
1385 xrow = MAX(0, xrow - n);
1386 if (xtop > 0)
1387 xtop = MAX(0, xtop - n);
1388 xoff = lbuf_indents(xb, xrow);
1389 mod = 1;
1390 break;
1391 case TK_CTL('d'):
1392 if (xrow == lbuf_len(xb) - 1)
1393 break;
1394 if (vi_arg1)
1395 vi_scroll = vi_arg1;
1396 n = vi_scroll ? vi_scroll : xrows / 2;
1397 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1398 if (xtop < lbuf_len(xb) - xrows)
1399 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1400 xoff = lbuf_indents(xb, xrow);
1401 mod = 1;
1402 break;
1403 case TK_CTL('z'):
1404 term_pos(xrows, 0);
1405 term_suspend();
1406 mod = 5;
1407 break;
1408 case 'u':
1409 if (!lbuf_undo(xb)) {
1410 lbuf_jump(xb, '*', &xrow, &xoff);
1411 mod = 1;
1412 } else {
1413 snprintf(vi_msg, sizeof(vi_msg), "undo failed");
1415 break;
1416 case TK_CTL('r'):
1417 if (!lbuf_redo(xb)) {
1418 lbuf_jump(xb, '*', &xrow, &xoff);
1419 mod = 1;
1420 } else {
1421 snprintf(vi_msg, sizeof(vi_msg), "redo failed");
1423 break;
1424 case TK_CTL('g'):
1425 vc_status();
1426 break;
1427 case TK_CTL('^'):
1428 ex_command("e #");
1429 mod = 1;
1430 break;
1431 case TK_CTL(']'):
1432 if (!vc_tag(0))
1433 mod = 1;
1434 break;
1435 case TK_CTL('t'):
1436 if (!ex_command("pop")) {
1437 vi_marksave();
1438 mod = 1;
1440 break;
1441 case TK_CTL('w'):
1442 k = vi_read();
1443 if (k == 's')
1444 if (!vi_wsplit())
1445 mod = 5;
1446 if (k == 'j' || k == 'k') {
1447 if (w_cnt > 1) {
1448 vi_switch(1 - w_cur);
1449 mod = 5;
1452 if (k == 'o')
1453 if (!vi_wonly())
1454 mod = 1;
1455 if (k == 'c')
1456 if (!vi_wclose())
1457 mod = 1;
1458 if (k == 'x')
1459 if (!vi_wswap())
1460 mod = 5;
1461 if (k == TK_CTL(']') || k == ']')
1462 if (!vc_tag(1))
1463 mod = 5;
1464 if (k == 'g') {
1465 int j = vi_read();
1466 if (j == 'f' || j == 'l')
1467 if (!vc_openpath(j == 'l', 1))
1468 mod = 5;
1469 if (j == 'd')
1470 if (!vc_definition(1))
1471 mod = 5;
1473 if (k == 'q') {
1474 int j = vi_read();
1475 if (isalpha(j) && !vc_ecmd(j, 1))
1476 mod = 5;
1478 break;
1479 case ':':
1480 ln = vi_prompt(":", &kmap, reg_getln(':'));
1481 if (ln && ln[0]) {
1482 reg_putln(':', ln);
1483 if (ln[0] != ':') {
1484 char *ln2 = uc_cat(":", ln);
1485 free(ln);
1486 ln = ln2;
1488 term_record();
1489 if (!ex_command(ln))
1490 mod = 5;
1491 term_commit();
1492 reg_put(':', ln, 1);
1494 free(ln);
1495 if (xquit)
1496 continue;
1497 break;
1498 case 'c':
1499 case 'd':
1500 case 'y':
1501 case '!':
1502 case '>':
1503 case '<':
1504 if (!vc_motion(c))
1505 mod = 1;
1506 break;
1507 case 'i':
1508 case 'I':
1509 case 'a':
1510 case 'A':
1511 case 'o':
1512 case 'O':
1513 if (!vc_insert(c))
1514 mod = 1;
1515 break;
1516 case 'J':
1517 if (!vc_join())
1518 mod = 1;
1519 break;
1520 case TK_CTL('l'):
1521 term_done();
1522 term_init();
1523 mod = 5;
1524 break;
1525 case 'm':
1526 if ((mark = vi_read()) > 0 && islower(mark))
1527 lbuf_mark(xb, mark, xrow, xoff);
1528 break;
1529 case 'p':
1530 case 'P':
1531 if (!vc_put(c))
1532 mod = 1;
1533 break;
1534 case 'z':
1535 k = vi_read();
1536 switch (k) {
1537 case '\n':
1538 xtop = vi_arg1 ? vi_arg1 : xrow;
1539 mod = 1;
1540 break;
1541 case '.':
1542 n = vi_arg1 ? vi_arg1 : xrow;
1543 xtop = MAX(0, n - xrows / 2);
1544 mod = 1;
1545 break;
1546 case '-':
1547 n = vi_arg1 ? vi_arg1 : xrow;
1548 xtop = MAX(0, n - xrows + 1);
1549 mod = 1;
1550 break;
1551 case 'l':
1552 case 'r':
1553 xtd = k == 'r' ? -1 : +1;
1554 mod = 1;
1555 break;
1556 case 'L':
1557 case 'R':
1558 xtd = k == 'R' ? -2 : +2;
1559 mod = 1;
1560 break;
1561 case 'e':
1562 case 'f':
1563 xkmap = k == 'e' ? 0 : xkmap_alt;
1564 break;
1565 case 'j':
1566 case 'k':
1567 if (!ex_command(k == 'j' ? "b +" : "b -"))
1568 mod = 1;
1569 break;
1570 case 'J':
1571 case 'K':
1572 if (!ex_command(k == 'J' ? "next" : "prev"))
1573 mod = 1;
1574 break;
1575 case 'D':
1576 if (!ex_command("b !"))
1577 mod = 1;
1578 break;
1580 break;
1581 case 'g':
1582 k = vi_read();
1583 if (k == '~' || k == 'u' || k == 'U')
1584 if (!vc_motion(k))
1585 mod = 2;
1586 if (k == 'a')
1587 vc_charinfo();
1588 if (k == 'd')
1589 if (!vc_definition(0))
1590 mod = 1;
1591 if (k == 'f' || k == 'l')
1592 if (!vc_openpath(k == 'l', 0))
1593 mod = 1;
1594 break;
1595 case 'x':
1596 vi_back(' ');
1597 if (!vc_motion('d'))
1598 mod = 2;
1599 break;
1600 case 'X':
1601 vi_back(TK_CTL('h'));
1602 if (!vc_motion('d'))
1603 mod = 2;
1604 break;
1605 case 'C':
1606 vi_back('$');
1607 if (!vc_motion('c'))
1608 mod = 1;
1609 break;
1610 case 'D':
1611 vi_back('$');
1612 if (!vc_motion('d'))
1613 mod = 2;
1614 break;
1615 case 'q':
1616 k = vi_read();
1617 if (isalpha(k) && !vc_ecmd(k, 0))
1618 mod = 5;
1619 break;
1620 case 'r':
1621 if (!vc_replace())
1622 mod = 2;
1623 break;
1624 case 's':
1625 vi_back(' ');
1626 if (!vc_motion('c'))
1627 mod = 1;
1628 break;
1629 case 'S':
1630 vi_back('c');
1631 if (!vc_motion('c'))
1632 mod = 1;
1633 break;
1634 case 'Y':
1635 vi_back('y');
1636 vc_motion('y');
1637 break;
1638 case 'Z':
1639 k = vi_read();
1640 if (k == 'Z')
1641 if (!ex_command("x"))
1642 mod = 1;
1643 break;
1644 case '~':
1645 vi_back(' ');
1646 if (!vc_motion('~'))
1647 mod = 2;
1648 break;
1649 case '.':
1650 vc_repeat();
1651 break;
1652 case '@':
1653 vc_execute();
1654 break;
1655 default:
1656 continue;
1658 cmd = term_cmd(&n);
1659 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1660 (c == 'g' && strchr("uU~", k))) {
1661 if (n + 1 < sizeof(rep_cmd)) {
1662 memcpy(rep_cmd, cmd, n);
1663 rep_len = n;
1664 rep_cmd[n] = '\0';
1665 reg_put('.', rep_cmd, 0);
1669 vi_wfix();
1670 if (mod)
1671 xcol = vi_off2col(xb, xrow, xoff);
1672 if (xcol >= xleft + xcols)
1673 xleft = xcol - xcols / 2;
1674 if (xcol < xleft)
1675 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1676 vi_wait();
1677 ru = (xru & 1) || ((xru & 2) && w_cnt > 1) || ((xru & 4) && opath != ex_path());
1678 if (mod & 4 && w_cnt > 1) {
1679 char msg[sizeof(vi_msg)];
1680 int id = w_cur;
1681 w_tmp = 1;
1682 vi_switch(1 - id);
1683 vi_wfix();
1684 strcpy(msg, vi_msg);
1685 if (ru)
1686 vc_status();
1687 vi_drawagain(vi_off2col(xb, xrow, xoff), 0);
1688 strcpy(vi_msg, msg);
1689 w_tmp = 0;
1690 vi_switch(id);
1692 if (ru && !vi_msg[0])
1693 vc_status();
1694 if (mod || xleft != oleft) {
1695 vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
1696 } else {
1697 if (xtop != otop)
1698 vi_drawupdate(xcol, otop);
1699 if (xhll && xrow != orow && orow >= xtop && orow < xtop + xrows)
1700 vi_drawrow(orow);
1701 if (xhll && xrow != orow)
1702 vi_drawrow(xrow);
1703 if (vi_msg[0])
1704 vi_drawmsg();
1706 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1707 ren_cursor(lbuf_get(xb, xrow), xcol)));
1708 lbuf_modified(xb);
1712 int main(int argc, char *argv[])
1714 int i;
1715 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1716 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1717 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1718 if (argv[i][1] == 's')
1719 xled = 0;
1720 if (argv[i][1] == 'e')
1721 xvis = 0;
1722 if (argv[i][1] == 'v')
1723 xvis = 1;
1724 if (argv[i][1] == 'h') {
1725 printf("usage: %s [options] [file...]\n\n", argv[0]);
1726 printf("options:\n");
1727 printf(" -v start in vi mode\n");
1728 printf(" -e start in ex mode\n");
1729 printf(" -s silent mode (for ex mode only)\n");
1730 return 0;
1733 dir_init();
1734 syn_init();
1735 tag_init();
1736 if (!ex_init(argv + i)) {
1737 if (xled || xvis)
1738 term_init();
1739 if (xvis)
1740 vi();
1741 else
1742 ex();
1743 if (xled || xvis)
1744 term_done();
1745 ex_done();
1747 free(w_path);
1748 reg_done();
1749 syn_done();
1750 dir_done();
1751 tag_done();
1752 return 0;