vi: redraw with ^l
[neatvi.git] / vi.c
blob10d150e3157ee61cf079455d13d392fe0958bef4
1 /*
2 * neatvi editor
4 * Copyright (C) 2015 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 char *vi_kmap; /* current insertion keymap */
21 static int vi_pcol; /* the column requested by | command */
22 static int vi_printed; /* ex_print() calls since the last command */
23 static int vi_scroll; /* scroll amount for ^f and ^d*/
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 /* redraw the screen */
44 static void vi_draw(int xcol)
46 int i;
47 term_record();
48 for (i = xtop; i < xtop + xrows; i++) {
49 char *s = lbuf_get(xb, i);
50 led_print(s ? s : (i ? "~" : ""), i - xtop);
52 vi_drawmsg();
53 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
54 term_commit();
57 /* update the screen by removing lines r1 to r2 before an input command */
58 static void vi_drawrm(int r1, int r2, int newln)
60 r1 = MIN(MAX(r1, xtop), xtop + xrows);
61 r2 = MIN(MAX(r2, xtop), xtop + xrows);
62 term_pos(r1 - xtop, 0);
63 term_room(r1 - r2 + newln);
66 static int vi_buf[128];
67 static int vi_buflen;
69 static int vi_read(void)
71 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
74 static void vi_back(int c)
76 if (vi_buflen < sizeof(vi_buf))
77 vi_buf[vi_buflen++] = c;
80 static char *vi_char(void)
82 return led_read(&vi_kmap);
85 static char *vi_prompt(char *msg, char **kmap)
87 term_pos(xrows, led_pos(msg, 0));
88 term_kill();
89 return led_prompt(msg, "", kmap);
92 /* read an ex input line */
93 char *ex_read(char *msg)
95 struct sbuf *sb;
96 char c;
97 if (xled) {
98 char *s = led_prompt(msg, "", &vi_kmap);
99 if (s)
100 term_chr('\n');
101 return s;
103 sb = sbuf_make();
104 while ((c = getchar()) != EOF && c != '\n')
105 sbuf_chr(sb, c);
106 if (c == EOF) {
107 sbuf_free(sb);
108 return NULL;
110 return sbuf_done(sb);
113 /* show an ex message */
114 void ex_show(char *msg)
116 if (xvis) {
117 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
118 } else if (xled) {
119 led_print(msg, -1);
120 term_chr('\n');
121 } else {
122 printf("%s", msg);
126 /* print an ex output line */
127 void ex_print(char *line)
129 if (xvis) {
130 vi_printed += line ? 1 : 2;
131 if (line)
132 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
133 if (line)
134 led_print(line, -1);
135 term_chr('\n');
136 } else {
137 if (line)
138 ex_show(line);
142 static int vi_yankbuf(void)
144 int c = vi_read();
145 if (c == '"')
146 return vi_read();
147 vi_back(c);
148 return 0;
151 static int vi_prefix(void)
153 int n = 0;
154 int c = vi_read();
155 if ((c >= '1' && c <= '9')) {
156 while (isdigit(c)) {
157 n = n * 10 + c - '0';
158 c = vi_read();
161 vi_back(c);
162 return n;
165 static int vi_col2off(struct lbuf *lb, int row, int col)
167 char *ln = lbuf_get(lb, row);
168 return ln ? ren_off(ln, col) : 0;
171 static int vi_off2col(struct lbuf *lb, int row, int off)
173 char *ln = lbuf_get(lb, row);
174 return ln ? ren_pos(ln, off) : 0;
177 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
179 int o = *off + dir;
180 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
181 return 1;
182 *off = o;
183 return 0;
186 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
188 char *ln = lbuf_get(lb, *row);
189 int col = ln ? ren_pos(ln, *off) : 0;
190 int o = ln ? ren_next(ln, col, dir) : -1;
191 if (o < 0)
192 return -1;
193 *off = ren_off(ln, o);
194 return 0;
197 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
199 strcpy(vi_charlast, cs);
200 vi_charcmd = cmd;
201 return lbuf_findchar(lb, cs, cmd, n, row, off);
204 static int vi_search(int cmd, int cnt, int *row, int *off)
206 int r = *row;
207 int o = *off;
208 int failed = 0;
209 int len = 0;
210 int i, dir;
211 char *soff = "";
212 if (cmd == '/' || cmd == '?') {
213 char sign[4] = {cmd};
214 char *kw = vi_prompt(sign, &vi_kmap);
215 if (!kw)
216 return 1;
217 xfinddir = cmd == '/' ? +1 : -1;
218 if (kw[0])
219 snprintf(xfindkwd, sizeof(xfindkwd), "%s", kw);
220 if (strchr(xfindkwd, cmd)) {
221 soff = strchr(xfindkwd, cmd) + 1;
222 *strchr(xfindkwd, cmd) = '\0';
224 free(kw);
226 dir = cmd == 'N' ? -xfinddir : xfinddir;
227 if (!xfindkwd[0] || !lbuf_len(xb))
228 return 1;
229 o = *off;
230 for (i = 0; i < cnt; i++) {
231 if (lbuf_search(xb, xfindkwd, dir, &r, &o, &len)) {
232 failed = 1;
233 break;
235 if (i + 1 < cnt && cmd == '/')
236 o += len;
238 if (!failed) {
239 *row = r;
240 *off = o;
241 while (soff[0] && isspace((unsigned char) soff[0]))
242 soff++;
243 if (soff[0]) {
244 *off = -1;
245 if (*row + atoi(soff) < 0 || *row + atoi(soff) >= lbuf_len(xb))
246 failed = 1;
247 else
248 *row += atoi(soff);
251 if (failed)
252 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", xfindkwd);
253 return failed;
256 /* read a line motion */
257 static int vi_motionln(int *row, int cmd)
259 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
260 int c = vi_read();
261 int mark, mark_row, mark_off;
262 switch (c) {
263 case '\n':
264 case '+':
265 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
266 break;
267 case '-':
268 *row = MAX(*row - cnt, 0);
269 break;
270 case '_':
271 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
272 break;
273 case '\'':
274 if ((mark = vi_read()) <= 0)
275 return -1;
276 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
277 return -1;
278 *row = mark_row;
279 break;
280 case 'j':
281 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
282 break;
283 case 'k':
284 *row = MAX(*row - cnt, 0);
285 break;
286 case 'G':
287 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
288 break;
289 case 'H':
290 if (lbuf_len(xb))
291 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
292 else
293 *row = 0;
294 break;
295 case 'L':
296 if (lbuf_len(xb))
297 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
298 else
299 *row = 0;
300 break;
301 case 'M':
302 if (lbuf_len(xb))
303 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
304 else
305 *row = 0;
306 break;
307 default:
308 if (c == cmd) {
309 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
310 break;
312 if (c == '%' && (vi_arg1 || vi_arg2)) {
313 if (cnt > 100)
314 return -1;
315 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
316 break;
318 vi_back(c);
319 return 0;
321 return c;
324 static char *vi_curword(struct lbuf *lb, int row, int off)
326 struct sbuf *sb;
327 char *ln = lbuf_get(lb, row);
328 char *beg, *end;
329 if (!ln)
330 return NULL;
331 beg = uc_chr(ln, ren_noeol(ln, off));
332 end = beg;
333 while (*end && uc_kind(end) == 1)
334 end = uc_next(end);
335 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
336 beg = uc_beg(ln, beg - 1);
337 if (beg >= end)
338 return NULL;
339 sb = sbuf_make();
340 sbuf_str(sb, "\\<");
341 sbuf_mem(sb, beg, end - beg);
342 sbuf_str(sb, "\\>");
343 return sbuf_done(sb);
346 /* read a motion */
347 static int vi_motion(int *row, int *off)
349 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
350 char *ln = lbuf_get(xb, *row);
351 int dir = dir_context(ln ? ln : "");
352 int mark, mark_row, mark_off;
353 char *cs;
354 int mv;
355 int i;
356 if ((mv = vi_motionln(row, 0))) {
357 *off = -1;
358 return mv;
360 mv = vi_read();
361 switch (mv) {
362 case 'f':
363 if (!(cs = vi_char()))
364 return -1;
365 if (vi_findchar(xb, cs, mv, cnt, row, off))
366 return -1;
367 break;
368 case 'F':
369 if (!(cs = vi_char()))
370 return -1;
371 if (vi_findchar(xb, cs, mv, cnt, row, off))
372 return -1;
373 break;
374 case ';':
375 if (!vi_charlast[0])
376 return -1;
377 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
378 return -1;
379 break;
380 case ',':
381 if (!vi_charlast[0])
382 return -1;
383 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
384 return -1;
385 break;
386 case 'h':
387 for (i = 0; i < cnt; i++)
388 if (vi_nextcol(xb, -1 * dir, row, off))
389 break;
390 break;
391 case 'l':
392 for (i = 0; i < cnt; i++)
393 if (vi_nextcol(xb, +1 * dir, row, off))
394 break;
395 break;
396 case 't':
397 if (!(cs = vi_char()))
398 return -1;
399 if (vi_findchar(xb, cs, mv, cnt, row, off))
400 return -1;
401 break;
402 case 'T':
403 if (!(cs = vi_char()))
404 return -1;
405 if (vi_findchar(xb, cs, mv, cnt, row, off))
406 return -1;
407 break;
408 case 'B':
409 for (i = 0; i < cnt; i++)
410 if (lbuf_wordend(xb, 1, -1, row, off))
411 break;
412 break;
413 case 'E':
414 for (i = 0; i < cnt; i++)
415 if (lbuf_wordend(xb, 1, +1, row, off))
416 break;
417 break;
418 case 'W':
419 for (i = 0; i < cnt; i++)
420 if (lbuf_wordbeg(xb, 1, +1, row, off))
421 break;
422 break;
423 case 'b':
424 for (i = 0; i < cnt; i++)
425 if (lbuf_wordend(xb, 0, -1, row, off))
426 break;
427 break;
428 case 'e':
429 for (i = 0; i < cnt; i++)
430 if (lbuf_wordend(xb, 0, +1, row, off))
431 break;
432 break;
433 case 'w':
434 for (i = 0; i < cnt; i++)
435 if (lbuf_wordbeg(xb, 0, +1, row, off))
436 break;
437 break;
438 case '{':
439 for (i = 0; i < cnt; i++)
440 if (lbuf_paragraphbeg(xb, -1, row, off))
441 break;
442 break;
443 case '}':
444 for (i = 0; i < cnt; i++)
445 if (lbuf_paragraphbeg(xb, +1, row, off))
446 break;
447 break;
448 case '[':
449 if (vi_read() != '[')
450 return -1;
451 for (i = 0; i < cnt; i++)
452 if (lbuf_sectionbeg(xb, -1, row, off))
453 break;
454 break;
455 case ']':
456 if (vi_read() != ']')
457 return -1;
458 for (i = 0; i < cnt; i++)
459 if (lbuf_sectionbeg(xb, +1, row, off))
460 break;
461 break;
462 case '0':
463 *off = 0;
464 break;
465 case '^':
466 *off = lbuf_indents(xb, *row);
467 break;
468 case '$':
469 *off = lbuf_eol(xb, *row);
470 break;
471 case '|':
472 *off = vi_col2off(xb, *row, cnt - 1);
473 vi_pcol = cnt - 1;
474 break;
475 case '/':
476 if (vi_search(mv, cnt, row, off))
477 return -1;
478 break;
479 case '?':
480 if (vi_search(mv, cnt, row, off))
481 return -1;
482 break;
483 case 'n':
484 if (vi_search(mv, cnt, row, off))
485 return -1;
486 break;
487 case 'N':
488 if (vi_search(mv, cnt, row, off))
489 return -1;
490 break;
491 case TK_CTL('a'):
492 if (!(cs = vi_curword(xb, *row, *off)))
493 return -1;
494 strcpy(xfindkwd, cs);
495 free(cs);
496 xfinddir = +1;
497 if (vi_search('n', cnt, row, off))
498 return -1;
499 break;
500 case ' ':
501 for (i = 0; i < cnt; i++)
502 if (vi_nextoff(xb, +1, row, off))
503 break;
504 break;
505 case 127:
506 case TK_CTL('h'):
507 for (i = 0; i < cnt; i++)
508 if (vi_nextoff(xb, -1, row, off))
509 break;
510 break;
511 case '`':
512 if ((mark = vi_read()) <= 0)
513 return -1;
514 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
515 return -1;
516 *row = mark_row;
517 *off = mark_off;
518 break;
519 case '%':
520 if (lbuf_pair(xb, row, off))
521 return -1;
522 break;
523 default:
524 vi_back(mv);
525 return 0;
527 return mv;
530 static void swap(int *a, int *b)
532 int t = *a;
533 *a = *b;
534 *b = t;
537 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
539 struct sbuf *sb;
540 char *s1, *s2, *s3;
541 if (r1 == r2)
542 return uc_sub(lbuf_get(lb, r1), o1, o2);
543 sb = sbuf_make();
544 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
545 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
546 s2 = lbuf_cp(lb, r1 + 1, r2);
547 sbuf_str(sb, s1);
548 sbuf_str(sb, s2);
549 sbuf_str(sb, s3);
550 free(s1);
551 free(s2);
552 free(s3);
553 return sbuf_done(sb);
556 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
558 char *region;
559 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
560 reg_put(vi_ybuf, region, lnmode);
561 free(region);
562 xrow = r1;
563 xoff = lnmode ? xoff : o1;
566 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
568 char *pref, *post;
569 char *region;
570 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
571 reg_put(vi_ybuf, region, lnmode);
572 free(region);
573 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
574 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
575 if (!lnmode) {
576 struct sbuf *sb = sbuf_make();
577 sbuf_str(sb, pref);
578 sbuf_str(sb, post);
579 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
580 sbuf_free(sb);
581 } else {
582 lbuf_edit(xb, NULL, r1, r2 + 1);
584 xrow = r1;
585 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
586 free(pref);
587 free(post);
590 static int linecount(char *s)
592 int n;
593 for (n = 0; s; n++)
594 if ((s = strchr(s, '\n')))
595 s++;
596 return n;
599 static int indentscopy(char *d, char *s, int len)
601 int i;
602 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
603 d[i] = s[i];
604 d[i] = '\0';
605 return i;
608 static char *vi_input(char *pref, char *post, int *row, int *off)
610 char ai[64] = "";
611 char *rep, *s;
612 struct sbuf *sb;
613 int last;
614 if (xai)
615 pref += indentscopy(ai, pref, sizeof(ai));
616 rep = led_input(pref, post, ai, xai ? sizeof(ai) - 1 : 0, &vi_kmap);
617 if (!rep)
618 return NULL;
619 sb = sbuf_make();
620 sbuf_str(sb, ai);
621 sbuf_str(sb, pref);
622 sbuf_str(sb, rep);
623 s = sbuf_buf(sb);
624 last = uc_lastline(s) - s;
625 *off = MAX(0, uc_slen(sbuf_buf(sb) + last) - 1);
626 if (last)
627 while (xai && (post[0] == ' ' || post[0] == '\t'))
628 post++;
629 sbuf_str(sb, post);
630 *row = linecount(sbuf_buf(sb)) - 1;
631 free(rep);
632 return sbuf_done(sb);
635 static char *vi_indents(char *ln)
637 struct sbuf *sb = sbuf_make();
638 while (xai && ln && (*ln == ' ' || *ln == '\t'))
639 sbuf_chr(sb, *ln++);
640 return sbuf_done(sb);
643 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
645 char *region;
646 int row, off;
647 char *rep;
648 char *pref, *post;
649 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
650 reg_put(vi_ybuf, region, lnmode);
651 free(region);
652 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
653 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
654 vi_drawrm(r1, r2, 0);
655 rep = vi_input(pref, post, &row, &off);
656 if (rep) {
657 lbuf_edit(xb, rep, r1, r2 + 1);
658 xrow = r1 + row - 1;
659 xoff = off;
660 free(rep);
662 free(pref);
663 free(post);
666 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
668 char *pref, *post;
669 char *region, *s;
670 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
671 s = region;
672 while (*s) {
673 int c = (unsigned char) s[0];
674 if (c <= 0x7f) {
675 if (cmd == 'u')
676 s[0] = tolower(c);
677 if (cmd == 'U')
678 s[0] = toupper(c);
679 if (cmd == '~')
680 s[0] = islower(c) ? toupper(c) : tolower(c);
682 s = uc_next(s);
684 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
685 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
686 if (!lnmode) {
687 struct sbuf *sb = sbuf_make();
688 sbuf_str(sb, pref);
689 sbuf_str(sb, region);
690 sbuf_str(sb, post);
691 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
692 sbuf_free(sb);
693 } else {
694 lbuf_edit(xb, region, r1, r2 + 1);
696 xrow = r2;
697 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
698 free(region);
699 free(pref);
700 free(post);
703 static void vi_pipe(int r1, int r2)
705 char *text;
706 char *rep;
707 char *kmap = NULL;
708 char *cmd = vi_prompt("!", &kmap);
709 if (!cmd)
710 return;
711 text = lbuf_cp(xb, r1, r2 + 1);
712 rep = cmd_pipe(cmd, text, 1, 1);
713 if (rep)
714 lbuf_edit(xb, rep, r1, r2 + 1);
715 free(cmd);
716 free(text);
717 free(rep);
720 static void vi_shift(int r1, int r2, int dir)
722 struct sbuf *sb;
723 char *ln;
724 int i;
725 for (i = r1; i <= r2; i++) {
726 if (!(ln = lbuf_get(xb, i)))
727 continue;
728 sb = sbuf_make();
729 if (dir > 0)
730 sbuf_chr(sb, '\t');
731 else
732 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
733 sbuf_str(sb, ln);
734 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
735 sbuf_free(sb);
737 xrow = r1;
738 xoff = lbuf_indents(xb, xrow);
741 static int vc_motion(int cmd)
743 int r1 = xrow, r2 = xrow; /* region rows */
744 int o1 = xoff, o2 = xoff; /* visual region columns */
745 int lnmode = 0; /* line-based region */
746 int mv;
747 vi_arg2 = vi_prefix();
748 if (vi_arg2 < 0)
749 return 1;
750 o1 = ren_noeol(lbuf_get(xb, r1), o1);
751 o2 = o1;
752 if ((mv = vi_motionln(&r2, cmd))) {
753 o2 = -1;
754 } else if (!(mv = vi_motion(&r2, &o2))) {
755 vi_read();
756 return 1;
758 if (mv < 0)
759 return 1;
760 lnmode = o2 < 0;
761 if (lnmode) {
762 o1 = 0;
763 o2 = lbuf_eol(xb, r2);
765 if (r1 > r2) {
766 swap(&r1, &r2);
767 swap(&o1, &o2);
769 if (r1 == r2 && o1 > o2)
770 swap(&o1, &o2);
771 o1 = ren_noeol(lbuf_get(xb, r1), o1);
772 if (!lnmode && strchr("fFtTeE%", mv))
773 if (o2 < lbuf_eol(xb, r2))
774 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
775 if (cmd == 'y')
776 vi_yank(r1, o1, r2, o2, lnmode);
777 if (cmd == 'd')
778 vi_delete(r1, o1, r2, o2, lnmode);
779 if (cmd == 'c')
780 vi_change(r1, o1, r2, o2, lnmode);
781 if (cmd == '~' || cmd == 'u' || cmd == 'U')
782 vi_case(r1, o1, r2, o2, lnmode, cmd);
783 if (cmd == '!')
784 vi_pipe(r1, r2);
785 if (cmd == '>' || cmd == '<')
786 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
787 return 0;
790 static int vc_insert(int cmd)
792 char *pref, *post;
793 char *ln = lbuf_get(xb, xrow);
794 int row, off = 0;
795 char *rep;
796 if (cmd == 'I')
797 xoff = lbuf_indents(xb, xrow);
798 if (cmd == 'A')
799 xoff = lbuf_eol(xb, xrow);
800 xoff = ren_noeol(ln, xoff);
801 if (cmd == 'o')
802 xrow += 1;
803 if (cmd == 'i' || cmd == 'I')
804 off = xoff;
805 if (cmd == 'a' || cmd == 'A')
806 off = xoff + 1;
807 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
808 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
809 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
810 rep = vi_input(pref, post, &row, &off);
811 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
812 lbuf_edit(xb, "\n", 0, 0);
813 if (rep) {
814 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
815 xrow += row - 1;
816 xoff = off;
817 free(rep);
819 free(pref);
820 free(post);
821 return !rep;
824 static int vc_put(int cmd)
826 int cnt = MAX(1, vi_arg1);
827 int lnmode;
828 char *buf = reg_get(vi_ybuf, &lnmode);
829 int i;
830 if (!buf) {
831 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
832 return 1;
834 if (lnmode) {
835 struct sbuf *sb = sbuf_make();
836 for (i = 0; i < cnt; i++)
837 sbuf_str(sb, buf);
838 if (!lbuf_len(xb))
839 lbuf_edit(xb, "\n", 0, 0);
840 if (cmd == 'p')
841 xrow++;
842 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
843 xoff = lbuf_indents(xb, xrow);
844 sbuf_free(sb);
845 } else {
846 struct sbuf *sb = sbuf_make();
847 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
848 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
849 char *s = uc_sub(ln, 0, off);
850 sbuf_str(sb, s);
851 free(s);
852 for (i = 0; i < cnt; i++)
853 sbuf_str(sb, buf);
854 s = uc_sub(ln, off, -1);
855 sbuf_str(sb, s);
856 free(s);
857 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
858 xoff = off + uc_slen(buf) * cnt - 1;
859 sbuf_free(sb);
861 return 0;
864 static int join_spaces(char *prev, char *next)
866 int prevlen = strlen(prev);
867 if (!prev[0])
868 return 0;
869 if (prev[prevlen - 1] == ' ' || next[0] == ')')
870 return 0;
871 return prev[prevlen - 1] == '.' ? 2 : 1;
874 static int vc_join(void)
876 struct sbuf *sb;
877 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
878 int beg = xrow;
879 int end = xrow + cnt;
880 int off = 0;
881 int i;
882 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
883 return 1;
884 sb = sbuf_make();
885 for (i = beg; i < end; i++) {
886 char *ln = lbuf_get(xb, i);
887 char *lnend = strchr(ln, '\n');
888 int spaces;
889 if (i > beg)
890 while (ln[0] == ' ' || ln[0] == '\t')
891 ln++;
892 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
893 off = uc_slen(sbuf_buf(sb));
894 while (spaces--)
895 sbuf_chr(sb, ' ');
896 sbuf_mem(sb, ln, lnend - ln);
898 sbuf_chr(sb, '\n');
899 lbuf_edit(xb, sbuf_buf(sb), beg, end);
900 xoff = off;
901 sbuf_free(sb);
902 return 0;
905 static int vi_scrollforeward(int cnt)
907 if (xtop >= lbuf_len(xb) - 1)
908 return 1;
909 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
910 xrow = MAX(xrow, xtop);
911 return 0;
914 static int vi_scrollbackward(int cnt)
916 if (xtop == 0)
917 return 1;
918 xtop = MAX(0, xtop - cnt);
919 xrow = MIN(xrow, xtop + xrows - 1);
920 return 0;
923 static void vc_status(void)
925 int col = vi_off2col(xb, xrow, xoff);
926 snprintf(vi_msg, sizeof(vi_msg),
927 "\"%s\"%c %d lines L%d C%d\n",
928 ex_path()[0] ? ex_path() : "unnamed",
929 lbuf_modified(xb) ? '*' : ' ',
930 lbuf_len(xb), xrow + 1,
931 ren_cursor(lbuf_get(xb, xrow), col) + 1);
934 static int vc_replace(void)
936 int cnt = MAX(1, vi_arg1);
937 char *cs = vi_char();
938 char *ln = lbuf_get(xb, xrow);
939 struct sbuf *sb;
940 char *pref, *post;
941 char *s;
942 int off, i;
943 if (!ln || !cs)
944 return 1;
945 off = ren_noeol(ln, xoff);
946 s = uc_chr(ln, off);
947 for (i = 0; s[0] != '\n' && i < cnt; i++)
948 s = uc_next(s);
949 if (i < cnt)
950 return 1;
951 pref = uc_sub(ln, 0, off);
952 post = uc_sub(ln, off + cnt, -1);
953 sb = sbuf_make();
954 sbuf_str(sb, pref);
955 for (i = 0; i < cnt; i++)
956 sbuf_str(sb, cs);
957 sbuf_str(sb, post);
958 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
959 off += cnt - 1;
960 xoff = off;
961 sbuf_free(sb);
962 free(pref);
963 free(post);
964 return 0;
967 static char rep_cmd[4096]; /* the last command */
968 static int rep_len;
970 static void vc_repeat(void)
972 term_push(rep_cmd, rep_len);
975 static void vc_execute(void)
977 static int exec_buf;
978 int lnmode;
979 int c = vi_read();
980 char *buf;
981 if (TK_INT(c))
982 return;
983 if (c == '@')
984 c = exec_buf;
985 exec_buf = c;
986 buf = reg_get(exec_buf, &lnmode);
987 if (buf)
988 term_push(buf, strlen(buf));
991 static void vi(void)
993 int xcol;
994 int mark;
995 char *ln;
996 char *kmap = NULL;
997 xtop = MAX(0, xrow - xrows / 2);
998 xoff = 0;
999 xcol = vi_off2col(xb, xrow, xoff);
1000 vi_draw(xcol);
1001 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1002 while (!xquit) {
1003 int redraw = 0;
1004 int nrow = xrow;
1005 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1006 int otop = xtop;
1007 int oleft = xleft;
1008 int mv, n;
1009 term_cmd(&n);
1010 vi_arg2 = 0;
1011 vi_ybuf = vi_yankbuf();
1012 vi_arg1 = vi_prefix();
1013 if (!vi_ybuf)
1014 vi_ybuf = vi_yankbuf();
1015 mv = vi_motion(&nrow, &noff);
1016 if (mv > 0) {
1017 if (strchr("\'`GHML/?{}[]nN", mv) ||
1018 (mv == '%' && noff < 0)) {
1019 lbuf_mark(xb, '\'', xrow, xoff);
1020 lbuf_mark(xb, '`', xrow, xoff);
1022 xrow = nrow;
1023 if (noff < 0 && !strchr("jk", mv))
1024 noff = lbuf_indents(xb, xrow);
1025 if (strchr("jk", mv))
1026 noff = vi_col2off(xb, xrow, xcol);
1027 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1028 if (!strchr("|jk", mv))
1029 xcol = vi_off2col(xb, xrow, xoff);
1030 if (mv == '|')
1031 xcol = vi_pcol;
1032 } else if (mv == 0) {
1033 char *cmd;
1034 int c = vi_read();
1035 int k;
1036 if (c <= 0)
1037 continue;
1038 lbuf_mark(xb, '*', xrow, xoff);
1039 switch (c) {
1040 case TK_CTL('b'):
1041 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1042 break;
1043 xoff = lbuf_indents(xb, xrow);
1044 redraw = 1;
1045 break;
1046 case TK_CTL('f'):
1047 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1048 break;
1049 xoff = lbuf_indents(xb, xrow);
1050 redraw = 1;
1051 break;
1052 case TK_CTL('e'):
1053 if (vi_scrollforeward(MAX(1, vi_arg1)))
1054 break;
1055 redraw = 1;
1056 break;
1057 case TK_CTL('y'):
1058 if (vi_scrollbackward(MAX(1, vi_arg1)))
1059 break;
1060 redraw = 1;
1061 break;
1062 case TK_CTL('u'):
1063 if (xrow == 0)
1064 break;
1065 if (vi_arg1)
1066 vi_scroll = vi_arg1;
1067 n = vi_scroll ? vi_scroll : xrows / 2;
1068 xrow = MAX(0, xrow - n);
1069 if (xtop > 0)
1070 xtop = MAX(0, xtop - n);
1071 redraw = 1;
1072 xoff = lbuf_indents(xb, xrow);
1073 break;
1074 case TK_CTL('d'):
1075 if (xrow == lbuf_len(xb) - 1)
1076 break;
1077 if (vi_arg1)
1078 vi_scroll = vi_arg1;
1079 n = vi_scroll ? vi_scroll : xrows / 2;
1080 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1081 if (xtop < lbuf_len(xb) - xrows)
1082 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1083 redraw = 1;
1084 xoff = lbuf_indents(xb, xrow);
1085 break;
1086 case TK_CTL('z'):
1087 term_pos(xrows, 0);
1088 term_suspend();
1089 redraw = 1;
1090 break;
1091 case 'u':
1092 if (!lbuf_undo(xb)) {
1093 lbuf_jump(xb, '*', &xrow, &xoff);
1094 redraw = 1;
1095 } else {
1096 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1098 break;
1099 case TK_CTL('r'):
1100 if (!lbuf_redo(xb)) {
1101 lbuf_jump(xb, '*', &xrow, &xoff);
1102 redraw = 1;
1103 } else {
1104 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1106 break;
1107 case TK_CTL('g'):
1108 vc_status();
1109 break;
1110 case TK_CTL('^'):
1111 ex_command("e #");
1112 redraw = 1;
1113 break;
1114 case ':':
1115 ln = vi_prompt(":", &kmap);
1116 if (ln && ln[0]) {
1117 ex_command(ln);
1118 redraw = 1;
1120 free(ln);
1121 if (xquit)
1122 continue;
1123 break;
1124 case 'c':
1125 case 'd':
1126 case 'y':
1127 case '!':
1128 case '>':
1129 case '<':
1130 if (!vc_motion(c))
1131 redraw = 1;
1132 break;
1133 case 'i':
1134 case 'I':
1135 case 'a':
1136 case 'A':
1137 case 'o':
1138 case 'O':
1139 if (!vc_insert(c))
1140 redraw = 1;
1141 break;
1142 case 'J':
1143 if (!vc_join())
1144 redraw = 1;
1145 break;
1146 case TK_CTL('l'):
1147 redraw = 1;
1148 break;
1149 case 'm':
1150 if ((mark = vi_read()) > 0 && islower(mark))
1151 lbuf_mark(xb, mark, xrow, xoff);
1152 break;
1153 case 'p':
1154 case 'P':
1155 if (!vc_put(c))
1156 redraw = 1;
1157 break;
1158 case 'z':
1159 k = vi_read();
1160 switch (k) {
1161 case '\n':
1162 xtop = vi_arg1 ? vi_arg1 : xrow;
1163 break;
1164 case '.':
1165 n = vi_arg1 ? vi_arg1 : xrow;
1166 xtop = MAX(0, n - xrows / 2);
1167 break;
1168 case '-':
1169 n = vi_arg1 ? vi_arg1 : xrow;
1170 xtop = MAX(0, n - xrows + 1);
1171 break;
1172 case 'l':
1173 case 'r':
1174 xdir = k == 'r' ? -1 : +1;
1175 break;
1176 case 'L':
1177 case 'R':
1178 xdir = k == 'R' ? -2 : +2;
1179 break;
1181 redraw = 1;
1182 break;
1183 case 'g':
1184 k = vi_read();
1185 if (k == '~' || k == 'u' || k == 'U')
1186 if (!vc_motion(k))
1187 redraw = 1;
1188 break;
1189 case 'x':
1190 vi_back(' ');
1191 if (!vc_motion('d'))
1192 redraw = 1;
1193 break;
1194 case 'X':
1195 vi_back(TK_CTL('h'));
1196 if (!vc_motion('d'))
1197 redraw = 1;
1198 break;
1199 case 'C':
1200 vi_back('$');
1201 if (!vc_motion('c'))
1202 redraw = 1;
1203 break;
1204 case 'D':
1205 vi_back('$');
1206 if (!vc_motion('d'))
1207 redraw = 1;
1208 break;
1209 case 'r':
1210 if (!vc_replace())
1211 redraw = 1;
1212 break;
1213 case 's':
1214 vi_back(' ');
1215 if (!vc_motion('c'))
1216 redraw = 1;
1217 break;
1218 case 'S':
1219 vi_back('c');
1220 if (!vc_motion('c'))
1221 redraw = 1;
1222 break;
1223 case 'Y':
1224 vi_back('y');
1225 if (!vc_motion('y'))
1226 redraw = 1;
1227 break;
1228 case 'Z':
1229 k = vi_read();
1230 if (k == 'Z')
1231 ex_command("x");
1232 break;
1233 case '~':
1234 vi_back(' ');
1235 if (!vc_motion('~'))
1236 redraw = 1;
1237 break;
1238 case '.':
1239 vc_repeat();
1240 break;
1241 case '@':
1242 vc_execute();
1243 break;
1244 default:
1245 continue;
1247 cmd = term_cmd(&n);
1248 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1249 if (n < sizeof(rep_cmd)) {
1250 memcpy(rep_cmd, cmd, n);
1251 rep_len = n;
1255 if (xrow < 0 || xrow >= lbuf_len(xb))
1256 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1257 if (xtop > xrow)
1258 xtop = xtop - xrows / 2 > xrow ?
1259 MAX(0, xrow - xrows / 2) : xrow;
1260 if (xtop + xrows <= xrow)
1261 xtop = xtop + xrows + xrows / 2 <= xrow ?
1262 xrow - xrows / 2 : xrow - xrows + 1;
1263 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1264 if (redraw)
1265 xcol = vi_off2col(xb, xrow, xoff);
1266 if (xcol >= xleft + xcols)
1267 xleft = xcol - xcols / 2;
1268 if (xcol < xleft)
1269 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1270 vi_wait();
1271 if (redraw || xtop != otop || xleft != oleft)
1272 vi_draw(xcol);
1273 if (vi_msg[0])
1274 vi_drawmsg();
1275 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1276 ren_cursor(lbuf_get(xb, xrow), xcol)));
1277 lbuf_modified(xb);
1279 term_pos(xrows, 0);
1280 term_kill();
1283 int main(int argc, char *argv[])
1285 int i;
1286 xvis = 1;
1287 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1288 if (argv[i][1] == 's')
1289 xled = 0;
1290 if (argv[i][1] == 'e')
1291 xvis = 0;
1292 if (argv[i][1] == 'v')
1293 xvis = 1;
1295 dir_init();
1296 syn_init();
1297 if (xled || xvis)
1298 term_init();
1299 if (!ex_init(argv + i)) {
1300 if (xvis)
1301 vi();
1302 else
1303 ex();
1304 if (xled || xvis)
1305 term_done();
1306 ex_done();
1308 reg_done();
1309 syn_done();
1310 dir_done();
1311 return 0;