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