conf.h: neatmail file type
[neatvi.git] / vi.c
blob4a33f6d12313b1d5bdeb9c41c154122cb8fd76dd
1 /*
2 * neatvi editor
4 * Copyright (C) 2015-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include "vi.h"
15 static char vi_msg[EXLEN]; /* current message */
16 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
17 static int vi_charcmd; /* the character finding command */
18 static int vi_arg1, vi_arg2; /* the first and second arguments */
19 static int vi_ybuf; /* current yank buffer */
20 static int vi_pcol; /* the column requested by | command */
21 static int vi_printed; /* ex_print() calls since the last command */
22 static int vi_scroll; /* scroll amount for ^f and ^d*/
23 static int vi_soset, vi_so; /* search offset; 1 in "/kw/1" */
25 static void vi_wait(void)
27 if (vi_printed > 1) {
28 free(ex_read("[enter to continue]"));
29 vi_msg[0] = '\0';
31 vi_printed = 0;
34 static void vi_drawmsg(void)
36 int oleft = xleft;
37 xleft = 0;
38 led_print(vi_msg, xrows);
39 vi_msg[0] = '\0';
40 xleft = oleft;
43 static void vi_drawrow(int row)
45 char *s = lbuf_get(xb, row);
46 led_print(s ? s : (row ? "~" : ""), row - xtop);
49 /* redraw the screen */
50 static void vi_drawagain(int xcol, int lineonly)
52 int i;
53 term_record();
54 for (i = xtop; i < xtop + xrows; i++)
55 if (!lineonly || i == xrow)
56 vi_drawrow(i);
57 vi_drawmsg();
58 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
59 term_commit();
62 /* update the screen */
63 static void vi_drawupdate(int xcol, int otop)
65 int i;
66 if (otop != xtop) {
67 term_record();
68 term_pos(0, 0);
69 term_room(otop - xtop);
70 if (xtop > otop) {
71 int n = MIN(xtop - otop, xrows);
72 for (i = 0; i < n; i++)
73 vi_drawrow(xtop + xrows - n + i);
74 } else {
75 int n = MIN(otop - xtop, xrows);
76 for (i = 0; i < n; i++)
77 vi_drawrow(xtop + i);
79 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
80 term_commit();
82 vi_drawmsg();
83 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
86 /* update the screen by removing lines r1 to r2 before an input command */
87 static void vi_drawrm(int r1, int r2, int newln)
89 r1 = MIN(MAX(r1, xtop), xtop + xrows);
90 r2 = MIN(MAX(r2, xtop), xtop + xrows);
91 term_pos(r1 - xtop, 0);
92 term_room(r1 - r2 + newln);
95 static int vi_buf[128];
96 static int vi_buflen;
98 static int vi_read(void)
100 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
103 static void vi_back(int c)
105 if (vi_buflen < sizeof(vi_buf))
106 vi_buf[vi_buflen++] = c;
109 static char *vi_char(void)
111 return led_read(ex_kmap());
114 static char *vi_prompt(char *msg, char **kmap)
116 char *r, *s;
117 term_pos(xrows, led_pos(msg, 0));
118 term_kill();
119 s = led_prompt(msg, "", kmap);
120 if (!s)
121 return NULL;
122 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
123 free(s);
124 return r;
127 /* read an ex input line */
128 char *ex_read(char *msg)
130 struct sbuf *sb;
131 char c;
132 if (xled) {
133 char *s = led_prompt(msg, "", ex_kmap());
134 if (s)
135 term_chr('\n');
136 return s;
138 sb = sbuf_make();
139 while ((c = getchar()) != EOF && c != '\n')
140 sbuf_chr(sb, c);
141 if (c == EOF) {
142 sbuf_free(sb);
143 return NULL;
145 return sbuf_done(sb);
148 /* show an ex message */
149 void ex_show(char *msg)
151 if (xvis) {
152 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
153 } else if (xled) {
154 led_print(msg, -1);
155 term_chr('\n');
156 } else {
157 printf("%s", msg);
161 /* print an ex output line */
162 void ex_print(char *line)
164 if (xvis) {
165 vi_printed += line ? 1 : 2;
166 if (line)
167 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
168 if (line)
169 led_print(line, -1);
170 term_chr('\n');
171 } else {
172 if (line)
173 ex_show(line);
177 static int vi_yankbuf(void)
179 int c = vi_read();
180 if (c == '"')
181 return vi_read();
182 vi_back(c);
183 return 0;
186 static int vi_prefix(void)
188 int n = 0;
189 int c = vi_read();
190 if ((c >= '1' && c <= '9')) {
191 while (isdigit(c)) {
192 n = n * 10 + c - '0';
193 c = vi_read();
196 vi_back(c);
197 return n;
200 static int vi_col2off(struct lbuf *lb, int row, int col)
202 char *ln = lbuf_get(lb, row);
203 return ln ? ren_off(ln, col) : 0;
206 static int vi_off2col(struct lbuf *lb, int row, int off)
208 char *ln = lbuf_get(lb, row);
209 return ln ? ren_pos(ln, off) : 0;
212 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
214 int o = *off + dir;
215 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
216 return 1;
217 *off = o;
218 return 0;
221 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
223 char *ln = lbuf_get(lb, *row);
224 int col = ln ? ren_pos(ln, *off) : 0;
225 int o = ln ? ren_next(ln, col, dir) : -1;
226 if (o < 0)
227 return -1;
228 *off = ren_off(ln, o);
229 return 0;
232 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
234 strcpy(vi_charlast, cs);
235 vi_charcmd = cmd;
236 return lbuf_findchar(lb, cs, cmd, n, row, off);
239 static int vi_search(int cmd, int cnt, int *row, int *off)
241 char *kwd;
242 int r = *row;
243 int o = *off;
244 int failed = 0;
245 int len = 0;
246 int i, dir;
247 if (cmd == '/' || cmd == '?') {
248 char sign[4] = {cmd};
249 struct sbuf *sb;
250 char *kw = vi_prompt(sign, ex_kmap());
251 char *re;
252 if (!kw)
253 return 1;
254 sb = sbuf_make();
255 sbuf_chr(sb, cmd);
256 sbuf_str(sb, kw);
257 free(kw);
258 kw = sbuf_buf(sb);
259 if ((re = re_read(&kw))) {
260 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
261 while (isspace(*kw))
262 kw++;
263 vi_soset = !!kw[0];
264 vi_so = atoi(kw);
265 free(re);
267 sbuf_free(sb);
269 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
270 return 1;
271 dir = cmd == 'N' ? -dir : dir;
272 o = *off;
273 for (i = 0; i < cnt; i++) {
274 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
275 failed = 1;
276 break;
278 if (i + 1 < cnt && cmd == '/')
279 o += len;
281 if (!failed) {
282 *row = r;
283 *off = o;
284 if (vi_soset) {
285 *off = -1;
286 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
287 failed = 1;
288 else
289 *row += vi_so;
292 if (failed)
293 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", kwd);
294 return failed;
297 /* read a line motion */
298 static int vi_motionln(int *row, int cmd)
300 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
301 int c = vi_read();
302 int mark, mark_row, mark_off;
303 switch (c) {
304 case '\n':
305 case '+':
306 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
307 break;
308 case '-':
309 *row = MAX(*row - cnt, 0);
310 break;
311 case '_':
312 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
313 break;
314 case '\'':
315 if ((mark = vi_read()) <= 0)
316 return -1;
317 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
318 return -1;
319 *row = mark_row;
320 break;
321 case 'j':
322 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
323 break;
324 case 'k':
325 *row = MAX(*row - cnt, 0);
326 break;
327 case 'G':
328 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
329 break;
330 case 'H':
331 if (lbuf_len(xb))
332 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
333 else
334 *row = 0;
335 break;
336 case 'L':
337 if (lbuf_len(xb))
338 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
339 else
340 *row = 0;
341 break;
342 case 'M':
343 if (lbuf_len(xb))
344 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
345 else
346 *row = 0;
347 break;
348 default:
349 if (c == cmd) {
350 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
351 break;
353 if (c == '%' && (vi_arg1 || vi_arg2)) {
354 if (cnt > 100)
355 return -1;
356 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
357 break;
359 vi_back(c);
360 return 0;
362 return c;
365 static char *vi_curword(struct lbuf *lb, int row, int off)
367 struct sbuf *sb;
368 char *ln = lbuf_get(lb, row);
369 char *beg, *end;
370 if (!ln)
371 return NULL;
372 beg = uc_chr(ln, ren_noeol(ln, off));
373 end = beg;
374 while (*end && uc_kind(end) == 1)
375 end = uc_next(end);
376 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
377 beg = uc_beg(ln, beg - 1);
378 if (beg >= end)
379 return NULL;
380 sb = sbuf_make();
381 sbuf_str(sb, "\\<");
382 sbuf_mem(sb, beg, end - beg);
383 sbuf_str(sb, "\\>");
384 return sbuf_done(sb);
387 /* read a motion */
388 static int vi_motion(int *row, int *off)
390 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
391 char *ln = lbuf_get(xb, *row);
392 int dir = dir_context(ln ? ln : "");
393 int mark, mark_row, mark_off;
394 char *cs;
395 int mv;
396 int i;
397 if ((mv = vi_motionln(row, 0))) {
398 *off = -1;
399 return mv;
401 mv = vi_read();
402 switch (mv) {
403 case 'f':
404 if (!(cs = vi_char()))
405 return -1;
406 if (vi_findchar(xb, cs, mv, cnt, row, off))
407 return -1;
408 break;
409 case 'F':
410 if (!(cs = vi_char()))
411 return -1;
412 if (vi_findchar(xb, cs, mv, cnt, row, off))
413 return -1;
414 break;
415 case ';':
416 if (!vi_charlast[0])
417 return -1;
418 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
419 return -1;
420 break;
421 case ',':
422 if (!vi_charlast[0])
423 return -1;
424 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
425 return -1;
426 break;
427 case 'h':
428 for (i = 0; i < cnt; i++)
429 if (vi_nextcol(xb, -1 * dir, row, off))
430 break;
431 break;
432 case 'l':
433 for (i = 0; i < cnt; i++)
434 if (vi_nextcol(xb, +1 * dir, row, off))
435 break;
436 break;
437 case 't':
438 if (!(cs = vi_char()))
439 return -1;
440 if (vi_findchar(xb, cs, mv, cnt, row, off))
441 return -1;
442 break;
443 case 'T':
444 if (!(cs = vi_char()))
445 return -1;
446 if (vi_findchar(xb, cs, mv, cnt, row, off))
447 return -1;
448 break;
449 case 'B':
450 for (i = 0; i < cnt; i++)
451 if (lbuf_wordend(xb, 1, -1, row, off))
452 break;
453 break;
454 case 'E':
455 for (i = 0; i < cnt; i++)
456 if (lbuf_wordend(xb, 1, +1, row, off))
457 break;
458 break;
459 case 'W':
460 for (i = 0; i < cnt; i++)
461 if (lbuf_wordbeg(xb, 1, +1, row, off))
462 break;
463 break;
464 case 'b':
465 for (i = 0; i < cnt; i++)
466 if (lbuf_wordend(xb, 0, -1, row, off))
467 break;
468 break;
469 case 'e':
470 for (i = 0; i < cnt; i++)
471 if (lbuf_wordend(xb, 0, +1, row, off))
472 break;
473 break;
474 case 'w':
475 for (i = 0; i < cnt; i++)
476 if (lbuf_wordbeg(xb, 0, +1, row, off))
477 break;
478 break;
479 case '{':
480 for (i = 0; i < cnt; i++)
481 if (lbuf_paragraphbeg(xb, -1, row, off))
482 break;
483 break;
484 case '}':
485 for (i = 0; i < cnt; i++)
486 if (lbuf_paragraphbeg(xb, +1, row, off))
487 break;
488 break;
489 case '[':
490 if (vi_read() != '[')
491 return -1;
492 for (i = 0; i < cnt; i++)
493 if (lbuf_sectionbeg(xb, -1, row, off))
494 break;
495 break;
496 case ']':
497 if (vi_read() != ']')
498 return -1;
499 for (i = 0; i < cnt; i++)
500 if (lbuf_sectionbeg(xb, +1, row, off))
501 break;
502 break;
503 case '0':
504 *off = 0;
505 break;
506 case '^':
507 *off = lbuf_indents(xb, *row);
508 break;
509 case '$':
510 *off = lbuf_eol(xb, *row);
511 break;
512 case '|':
513 *off = vi_col2off(xb, *row, cnt - 1);
514 vi_pcol = cnt - 1;
515 break;
516 case '/':
517 if (vi_search(mv, cnt, row, off))
518 return -1;
519 break;
520 case '?':
521 if (vi_search(mv, cnt, row, off))
522 return -1;
523 break;
524 case 'n':
525 if (vi_search(mv, cnt, row, off))
526 return -1;
527 break;
528 case 'N':
529 if (vi_search(mv, cnt, row, off))
530 return -1;
531 break;
532 case TK_CTL('a'):
533 if (!(cs = vi_curword(xb, *row, *off)))
534 return -1;
535 ex_kwdset(cs, +1);
536 free(cs);
537 if (vi_search('n', cnt, row, off))
538 return -1;
539 break;
540 case ' ':
541 for (i = 0; i < cnt; i++)
542 if (vi_nextoff(xb, +1, row, off))
543 break;
544 break;
545 case 127:
546 case TK_CTL('h'):
547 for (i = 0; i < cnt; i++)
548 if (vi_nextoff(xb, -1, row, off))
549 break;
550 break;
551 case '`':
552 if ((mark = vi_read()) <= 0)
553 return -1;
554 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
555 return -1;
556 *row = mark_row;
557 *off = mark_off;
558 break;
559 case '%':
560 if (lbuf_pair(xb, row, off))
561 return -1;
562 break;
563 default:
564 vi_back(mv);
565 return 0;
567 return mv;
570 static void swap(int *a, int *b)
572 int t = *a;
573 *a = *b;
574 *b = t;
577 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
579 struct sbuf *sb;
580 char *s1, *s2, *s3;
581 if (r1 == r2)
582 return uc_sub(lbuf_get(lb, r1), o1, o2);
583 sb = sbuf_make();
584 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
585 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
586 s2 = lbuf_cp(lb, r1 + 1, r2);
587 sbuf_str(sb, s1);
588 sbuf_str(sb, s2);
589 sbuf_str(sb, s3);
590 free(s1);
591 free(s2);
592 free(s3);
593 return sbuf_done(sb);
596 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
598 char *region;
599 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
600 reg_put(vi_ybuf, region, lnmode);
601 free(region);
602 xrow = r1;
603 xoff = lnmode ? xoff : o1;
606 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
608 char *pref, *post;
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 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
614 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
615 if (!lnmode) {
616 struct sbuf *sb = sbuf_make();
617 sbuf_str(sb, pref);
618 sbuf_str(sb, post);
619 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
620 sbuf_free(sb);
621 } else {
622 lbuf_edit(xb, NULL, r1, r2 + 1);
624 xrow = r1;
625 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
626 free(pref);
627 free(post);
630 static int linecount(char *s)
632 int n;
633 for (n = 0; s; n++)
634 if ((s = strchr(s, '\n')))
635 s++;
636 return n;
639 static int charcount(char *text, char *post)
641 int tlen = strlen(text);
642 int plen = strlen(post);
643 char *nl = text;
644 int i;
645 if (tlen < plen)
646 return 0;
647 for (i = 0; i < tlen - plen; i++)
648 if (text[i] == '\n')
649 nl = text + i + 1;
650 return uc_slen(nl) - uc_slen(post);
653 static char *vi_input(char *pref, char *post, int *row, int *off)
655 char *rep = led_input(pref, post, ex_kmap());
656 if (!rep)
657 return NULL;
658 *row = linecount(rep) - 1;
659 *off = charcount(rep, post) - 1;
660 if (*off < 0)
661 *off = 0;
662 return rep;
665 static char *vi_indents(char *ln)
667 struct sbuf *sb = sbuf_make();
668 while (xai && ln && (*ln == ' ' || *ln == '\t'))
669 sbuf_chr(sb, *ln++);
670 return sbuf_done(sb);
673 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
675 char *region;
676 int row, off;
677 char *rep;
678 char *pref, *post;
679 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
680 reg_put(vi_ybuf, region, lnmode);
681 free(region);
682 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
683 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
684 vi_drawrm(r1, r2, 0);
685 rep = vi_input(pref, post, &row, &off);
686 if (rep) {
687 lbuf_edit(xb, rep, r1, r2 + 1);
688 xrow = r1 + row - 1;
689 xoff = off;
690 free(rep);
692 free(pref);
693 free(post);
696 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
698 char *pref, *post;
699 char *region, *s;
700 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
701 s = region;
702 while (*s) {
703 int c = (unsigned char) s[0];
704 if (c <= 0x7f) {
705 if (cmd == 'u')
706 s[0] = tolower(c);
707 if (cmd == 'U')
708 s[0] = toupper(c);
709 if (cmd == '~')
710 s[0] = islower(c) ? toupper(c) : tolower(c);
712 s = uc_next(s);
714 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
715 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
716 if (!lnmode) {
717 struct sbuf *sb = sbuf_make();
718 sbuf_str(sb, pref);
719 sbuf_str(sb, region);
720 sbuf_str(sb, post);
721 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
722 sbuf_free(sb);
723 } else {
724 lbuf_edit(xb, region, r1, r2 + 1);
726 xrow = r2;
727 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
728 free(region);
729 free(pref);
730 free(post);
733 static void vi_pipe(int r1, int r2)
735 char *text;
736 char *rep;
737 char *kmap = NULL;
738 char *cmd = vi_prompt("!", &kmap);
739 if (!cmd)
740 return;
741 text = lbuf_cp(xb, r1, r2 + 1);
742 rep = cmd_pipe(cmd, text, 1, 1);
743 if (rep)
744 lbuf_edit(xb, rep, r1, r2 + 1);
745 free(cmd);
746 free(text);
747 free(rep);
750 static void vi_shift(int r1, int r2, int dir)
752 struct sbuf *sb;
753 char *ln;
754 int i;
755 for (i = r1; i <= r2; i++) {
756 if (!(ln = lbuf_get(xb, i)))
757 continue;
758 sb = sbuf_make();
759 if (dir > 0)
760 sbuf_chr(sb, '\t');
761 else
762 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
763 sbuf_str(sb, ln);
764 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
765 sbuf_free(sb);
767 xrow = r1;
768 xoff = lbuf_indents(xb, xrow);
771 static int vc_motion(int cmd)
773 int r1 = xrow, r2 = xrow; /* region rows */
774 int o1 = xoff, o2 = xoff; /* visual region columns */
775 int lnmode = 0; /* line-based region */
776 int mv;
777 vi_arg2 = vi_prefix();
778 if (vi_arg2 < 0)
779 return 1;
780 o1 = ren_noeol(lbuf_get(xb, r1), o1);
781 o2 = o1;
782 if ((mv = vi_motionln(&r2, cmd))) {
783 o2 = -1;
784 } else if (!(mv = vi_motion(&r2, &o2))) {
785 vi_read();
786 return 1;
788 if (mv < 0)
789 return 1;
790 lnmode = o2 < 0;
791 if (lnmode) {
792 o1 = 0;
793 o2 = lbuf_eol(xb, r2);
795 if (r1 > r2) {
796 swap(&r1, &r2);
797 swap(&o1, &o2);
799 if (r1 == r2 && o1 > o2)
800 swap(&o1, &o2);
801 o1 = ren_noeol(lbuf_get(xb, r1), o1);
802 if (!lnmode && strchr("fFtTeE%", mv))
803 if (o2 < lbuf_eol(xb, r2))
804 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
805 if (cmd == 'y')
806 vi_yank(r1, o1, r2, o2, lnmode);
807 if (cmd == 'd')
808 vi_delete(r1, o1, r2, o2, lnmode);
809 if (cmd == 'c')
810 vi_change(r1, o1, r2, o2, lnmode);
811 if (cmd == '~' || cmd == 'u' || cmd == 'U')
812 vi_case(r1, o1, r2, o2, lnmode, cmd);
813 if (cmd == '!')
814 vi_pipe(r1, r2);
815 if (cmd == '>' || cmd == '<')
816 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
817 return 0;
820 static int vc_insert(int cmd)
822 char *pref, *post;
823 char *ln = lbuf_get(xb, xrow);
824 int row, off = 0;
825 char *rep;
826 if (cmd == 'I')
827 xoff = lbuf_indents(xb, xrow);
828 if (cmd == 'A')
829 xoff = lbuf_eol(xb, xrow);
830 xoff = ren_noeol(ln, xoff);
831 if (cmd == 'o')
832 xrow += 1;
833 if (cmd == 'i' || cmd == 'I')
834 off = xoff;
835 if (cmd == 'a' || cmd == 'A')
836 off = xoff + 1;
837 if (ln && ln[0] == '\n')
838 off = 0;
839 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
840 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
841 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
842 rep = vi_input(pref, post, &row, &off);
843 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
844 lbuf_edit(xb, "\n", 0, 0);
845 if (rep) {
846 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
847 xrow += row - 1;
848 xoff = off;
849 free(rep);
851 free(pref);
852 free(post);
853 return !rep;
856 static int vc_put(int cmd)
858 int cnt = MAX(1, vi_arg1);
859 int lnmode;
860 char *buf = reg_get(vi_ybuf, &lnmode);
861 int i;
862 if (!buf) {
863 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
864 return 1;
866 if (lnmode) {
867 struct sbuf *sb = sbuf_make();
868 for (i = 0; i < cnt; i++)
869 sbuf_str(sb, buf);
870 if (!lbuf_len(xb))
871 lbuf_edit(xb, "\n", 0, 0);
872 if (cmd == 'p')
873 xrow++;
874 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
875 xoff = lbuf_indents(xb, xrow);
876 sbuf_free(sb);
877 } else {
878 struct sbuf *sb = sbuf_make();
879 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
880 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
881 char *s = uc_sub(ln, 0, off);
882 sbuf_str(sb, s);
883 free(s);
884 for (i = 0; i < cnt; i++)
885 sbuf_str(sb, buf);
886 s = uc_sub(ln, off, -1);
887 sbuf_str(sb, s);
888 free(s);
889 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
890 xoff = off + uc_slen(buf) * cnt - 1;
891 sbuf_free(sb);
893 return 0;
896 static int join_spaces(char *prev, char *next)
898 int prevlen = strlen(prev);
899 if (!prev[0])
900 return 0;
901 if (prev[prevlen - 1] == ' ' || next[0] == ')')
902 return 0;
903 return prev[prevlen - 1] == '.' ? 2 : 1;
906 static int vc_join(void)
908 struct sbuf *sb;
909 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
910 int beg = xrow;
911 int end = xrow + cnt;
912 int off = 0;
913 int i;
914 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
915 return 1;
916 sb = sbuf_make();
917 for (i = beg; i < end; i++) {
918 char *ln = lbuf_get(xb, i);
919 char *lnend = strchr(ln, '\n');
920 int spaces;
921 if (i > beg)
922 while (ln[0] == ' ' || ln[0] == '\t')
923 ln++;
924 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
925 off = uc_slen(sbuf_buf(sb));
926 while (spaces--)
927 sbuf_chr(sb, ' ');
928 sbuf_mem(sb, ln, lnend - ln);
930 sbuf_chr(sb, '\n');
931 lbuf_edit(xb, sbuf_buf(sb), beg, end);
932 xoff = off;
933 sbuf_free(sb);
934 return 0;
937 static int vi_scrollforeward(int cnt)
939 if (xtop >= lbuf_len(xb) - 1)
940 return 1;
941 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
942 xrow = MAX(xrow, xtop);
943 return 0;
946 static int vi_scrollbackward(int cnt)
948 if (xtop == 0)
949 return 1;
950 xtop = MAX(0, xtop - cnt);
951 xrow = MIN(xrow, xtop + xrows - 1);
952 return 0;
955 static void vc_status(void)
957 int col = vi_off2col(xb, xrow, xoff);
958 snprintf(vi_msg, sizeof(vi_msg),
959 "\"%s\"%c %d lines L%d C%d\n",
960 ex_path()[0] ? ex_path() : "unnamed",
961 lbuf_modified(xb) ? '*' : ' ',
962 lbuf_len(xb), xrow + 1,
963 ren_cursor(lbuf_get(xb, xrow), col) + 1);
966 static int vc_replace(void)
968 int cnt = MAX(1, vi_arg1);
969 char *cs = vi_char();
970 char *ln = lbuf_get(xb, xrow);
971 struct sbuf *sb;
972 char *pref, *post;
973 char *s;
974 int off, i;
975 if (!ln || !cs)
976 return 1;
977 off = ren_noeol(ln, xoff);
978 s = uc_chr(ln, off);
979 for (i = 0; s[0] != '\n' && i < cnt; i++)
980 s = uc_next(s);
981 if (i < cnt)
982 return 1;
983 pref = uc_sub(ln, 0, off);
984 post = uc_sub(ln, off + cnt, -1);
985 sb = sbuf_make();
986 sbuf_str(sb, pref);
987 for (i = 0; i < cnt; i++)
988 sbuf_str(sb, cs);
989 sbuf_str(sb, post);
990 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
991 off += cnt - 1;
992 xoff = off;
993 sbuf_free(sb);
994 free(pref);
995 free(post);
996 return 0;
999 static char rep_cmd[4096]; /* the last command */
1000 static int rep_len;
1002 static void vc_repeat(void)
1004 term_push(rep_cmd, rep_len);
1007 static void vc_execute(void)
1009 static int exec_buf;
1010 int lnmode;
1011 int c = vi_read();
1012 char *buf;
1013 if (TK_INT(c))
1014 return;
1015 if (c == '@')
1016 c = exec_buf;
1017 exec_buf = c;
1018 buf = reg_get(exec_buf, &lnmode);
1019 if (buf)
1020 term_push(buf, strlen(buf));
1023 static void vi(void)
1025 int xcol;
1026 int mark;
1027 char *ln;
1028 char *kmap = NULL;
1029 xtop = MAX(0, xrow - xrows / 2);
1030 xoff = 0;
1031 xcol = vi_off2col(xb, xrow, xoff);
1032 vi_drawagain(xcol, 0);
1033 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1034 while (!xquit) {
1035 int mod = 0; /* screen should be redrawn */
1036 int nrow = xrow;
1037 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1038 int otop = xtop;
1039 int oleft = xleft;
1040 int mv, n;
1041 term_cmd(&n);
1042 vi_arg2 = 0;
1043 vi_ybuf = vi_yankbuf();
1044 vi_arg1 = vi_prefix();
1045 if (!vi_ybuf)
1046 vi_ybuf = vi_yankbuf();
1047 mv = vi_motion(&nrow, &noff);
1048 if (mv > 0) {
1049 if (strchr("\'`GHML/?{}[]nN", mv) ||
1050 (mv == '%' && noff < 0)) {
1051 lbuf_mark(xb, '\'', xrow, xoff);
1052 lbuf_mark(xb, '`', xrow, xoff);
1054 xrow = nrow;
1055 if (noff < 0 && !strchr("jk", mv))
1056 noff = lbuf_indents(xb, xrow);
1057 if (strchr("jk", mv))
1058 noff = vi_col2off(xb, xrow, xcol);
1059 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1060 if (!strchr("|jk", mv))
1061 xcol = vi_off2col(xb, xrow, xoff);
1062 if (mv == '|')
1063 xcol = vi_pcol;
1064 } else if (mv == 0) {
1065 char *cmd;
1066 int c = vi_read();
1067 int k;
1068 if (c <= 0)
1069 continue;
1070 lbuf_mark(xb, '*', xrow, xoff);
1071 switch (c) {
1072 case TK_CTL('b'):
1073 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1074 break;
1075 xoff = lbuf_indents(xb, xrow);
1076 break;
1077 case TK_CTL('f'):
1078 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1079 break;
1080 xoff = lbuf_indents(xb, xrow);
1081 break;
1082 case TK_CTL('e'):
1083 if (vi_scrollforeward(MAX(1, vi_arg1)))
1084 break;
1085 break;
1086 case TK_CTL('y'):
1087 if (vi_scrollbackward(MAX(1, vi_arg1)))
1088 break;
1089 break;
1090 case TK_CTL('u'):
1091 if (xrow == 0)
1092 break;
1093 if (vi_arg1)
1094 vi_scroll = vi_arg1;
1095 n = vi_scroll ? vi_scroll : xrows / 2;
1096 xrow = MAX(0, xrow - n);
1097 if (xtop > 0)
1098 xtop = MAX(0, xtop - n);
1099 xoff = lbuf_indents(xb, xrow);
1100 break;
1101 case TK_CTL('d'):
1102 if (xrow == lbuf_len(xb) - 1)
1103 break;
1104 if (vi_arg1)
1105 vi_scroll = vi_arg1;
1106 n = vi_scroll ? vi_scroll : xrows / 2;
1107 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1108 if (xtop < lbuf_len(xb) - xrows)
1109 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1110 xoff = lbuf_indents(xb, xrow);
1111 break;
1112 case TK_CTL('z'):
1113 term_pos(xrows, 0);
1114 term_suspend();
1115 mod = 1;
1116 break;
1117 case 'u':
1118 if (!lbuf_undo(xb)) {
1119 lbuf_jump(xb, '*', &xrow, &xoff);
1120 mod = 1;
1121 } else {
1122 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1124 break;
1125 case TK_CTL('r'):
1126 if (!lbuf_redo(xb)) {
1127 lbuf_jump(xb, '*', &xrow, &xoff);
1128 mod = 1;
1129 } else {
1130 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1132 break;
1133 case TK_CTL('g'):
1134 vc_status();
1135 break;
1136 case TK_CTL('^'):
1137 ex_command("e #");
1138 mod = 1;
1139 break;
1140 case ':':
1141 ln = vi_prompt(":", &kmap);
1142 if (ln && ln[0]) {
1143 ex_command(ln);
1144 mod = 1;
1146 free(ln);
1147 if (xquit)
1148 continue;
1149 break;
1150 case 'c':
1151 case 'd':
1152 case 'y':
1153 case '!':
1154 case '>':
1155 case '<':
1156 if (!vc_motion(c))
1157 mod = 1;
1158 break;
1159 case 'i':
1160 case 'I':
1161 case 'a':
1162 case 'A':
1163 case 'o':
1164 case 'O':
1165 if (!vc_insert(c))
1166 mod = 1;
1167 break;
1168 case 'J':
1169 if (!vc_join())
1170 mod = 1;
1171 break;
1172 case TK_CTL('l'):
1173 mod = 1;
1174 break;
1175 case 'm':
1176 if ((mark = vi_read()) > 0 && islower(mark))
1177 lbuf_mark(xb, mark, xrow, xoff);
1178 break;
1179 case 'p':
1180 case 'P':
1181 if (!vc_put(c))
1182 mod = 1;
1183 break;
1184 case 'z':
1185 k = vi_read();
1186 switch (k) {
1187 case '\n':
1188 xtop = vi_arg1 ? vi_arg1 : xrow;
1189 break;
1190 case '.':
1191 n = vi_arg1 ? vi_arg1 : xrow;
1192 xtop = MAX(0, n - xrows / 2);
1193 break;
1194 case '-':
1195 n = vi_arg1 ? vi_arg1 : xrow;
1196 xtop = MAX(0, n - xrows + 1);
1197 break;
1198 case 'l':
1199 case 'r':
1200 xdir = k == 'r' ? -1 : +1;
1201 break;
1202 case 'L':
1203 case 'R':
1204 xdir = k == 'R' ? -2 : +2;
1205 break;
1207 mod = 1;
1208 break;
1209 case 'g':
1210 k = vi_read();
1211 if (k == '~' || k == 'u' || k == 'U')
1212 if (!vc_motion(k))
1213 mod = 1;
1214 break;
1215 case 'x':
1216 vi_back(' ');
1217 if (!vc_motion('d'))
1218 mod = 2;
1219 break;
1220 case 'X':
1221 vi_back(TK_CTL('h'));
1222 if (!vc_motion('d'))
1223 mod = 2;
1224 break;
1225 case 'C':
1226 vi_back('$');
1227 if (!vc_motion('c'))
1228 mod = 1;
1229 break;
1230 case 'D':
1231 vi_back('$');
1232 if (!vc_motion('d'))
1233 mod = 2;
1234 break;
1235 case 'r':
1236 if (!vc_replace())
1237 mod = 2;
1238 break;
1239 case 's':
1240 vi_back(' ');
1241 if (!vc_motion('c'))
1242 mod = 1;
1243 break;
1244 case 'S':
1245 vi_back('c');
1246 if (!vc_motion('c'))
1247 mod = 1;
1248 break;
1249 case 'Y':
1250 vi_back('y');
1251 vc_motion('y');
1252 break;
1253 case 'Z':
1254 k = vi_read();
1255 if (k == 'Z')
1256 ex_command("x");
1257 break;
1258 case '~':
1259 vi_back(' ');
1260 if (!vc_motion('~'))
1261 mod = 2;
1262 break;
1263 case '.':
1264 vc_repeat();
1265 break;
1266 case '@':
1267 vc_execute();
1268 break;
1269 default:
1270 continue;
1272 cmd = term_cmd(&n);
1273 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1274 if (n < sizeof(rep_cmd)) {
1275 memcpy(rep_cmd, cmd, n);
1276 rep_len = n;
1280 if (xrow < 0 || xrow >= lbuf_len(xb))
1281 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1282 if (xtop > xrow)
1283 xtop = xtop - xrows / 2 > xrow ?
1284 MAX(0, xrow - xrows / 2) : xrow;
1285 if (xtop + xrows <= xrow)
1286 xtop = xtop + xrows + xrows / 2 <= xrow ?
1287 xrow - xrows / 2 : xrow - xrows + 1;
1288 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1289 if (mod)
1290 xcol = vi_off2col(xb, xrow, xoff);
1291 if (xcol >= xleft + xcols)
1292 xleft = xcol - xcols / 2;
1293 if (xcol < xleft)
1294 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1295 vi_wait();
1296 if (mod || xleft != oleft)
1297 vi_drawagain(xcol, mod == 2);
1298 else if (xtop != otop)
1299 vi_drawupdate(xcol, otop);
1300 if (vi_msg[0])
1301 vi_drawmsg();
1302 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1303 ren_cursor(lbuf_get(xb, xrow), xcol)));
1304 lbuf_modified(xb);
1306 term_pos(xrows, 0);
1307 term_kill();
1310 int main(int argc, char *argv[])
1312 int i;
1313 xvis = 1;
1314 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1315 if (argv[i][1] == 's')
1316 xled = 0;
1317 if (argv[i][1] == 'e')
1318 xvis = 0;
1319 if (argv[i][1] == 'v')
1320 xvis = 1;
1322 dir_init();
1323 syn_init();
1324 if (xled || xvis)
1325 term_init();
1326 if (!ex_init(argv + i)) {
1327 if (xvis)
1328 vi();
1329 else
1330 ex();
1331 if (xled || xvis)
1332 term_done();
1333 ex_done();
1335 reg_done();
1336 syn_done();
1337 dir_done();
1338 return 0;