ex: basic global command
[neatvi.git] / vi.c
blobab05e588234f564aa0f5e9ea148d4465caf4e749
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 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*/
24 static void vi_wait(void)
26 if (vi_printed > 1) {
27 free(ex_read("[enter to continue]"));
28 vi_msg[0] = '\0';
30 vi_printed = 0;
33 static void vi_drawmsg(void)
35 int oleft = xleft;
36 xleft = 0;
37 led_print(vi_msg, xrows);
38 vi_msg[0] = '\0';
39 xleft = oleft;
42 /* redraw the screen */
43 static void vi_draw(int xcol)
45 int i;
46 term_record();
47 for (i = xtop; i < xtop + xrows; i++) {
48 char *s = lbuf_get(xb, i);
49 led_print(s ? s : (i ? "~" : ""), i - xtop);
51 vi_drawmsg();
52 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
53 term_commit();
56 /* update the screen by removing lines r1 to r2 before an input command */
57 static void vi_drawrm(int r1, int r2, int newln)
59 r1 = MIN(MAX(r1, xtop), xtop + xrows);
60 r2 = MIN(MAX(r2, xtop), xtop + xrows);
61 term_pos(r1 - xtop, 0);
62 term_room(r1 - r2 + newln);
65 static int vi_buf[128];
66 static int vi_buflen;
68 static int vi_read(void)
70 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
73 static void vi_back(int c)
75 if (vi_buflen < sizeof(vi_buf))
76 vi_buf[vi_buflen++] = c;
79 static char *vi_char(void)
81 return led_read(ex_kmap());
84 static char *vi_prompt(char *msg, char **kmap)
86 char *r, *s;
87 term_pos(xrows, led_pos(msg, 0));
88 term_kill();
89 s = led_prompt(msg, "", kmap);
90 if (!s)
91 return NULL;
92 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
93 free(s);
94 return r;
97 /* read an ex input line */
98 char *ex_read(char *msg)
100 struct sbuf *sb;
101 char c;
102 if (xled) {
103 char *s = led_prompt(msg, "", ex_kmap());
104 if (s)
105 term_chr('\n');
106 return s;
108 sb = sbuf_make();
109 while ((c = getchar()) != EOF && c != '\n')
110 sbuf_chr(sb, c);
111 if (c == EOF) {
112 sbuf_free(sb);
113 return NULL;
115 return sbuf_done(sb);
118 /* show an ex message */
119 void ex_show(char *msg)
121 if (xvis) {
122 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
123 } else if (xled) {
124 led_print(msg, -1);
125 term_chr('\n');
126 } else {
127 printf("%s", msg);
131 /* print an ex output line */
132 void ex_print(char *line)
134 if (xvis) {
135 vi_printed += line ? 1 : 2;
136 if (line)
137 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
138 if (line)
139 led_print(line, -1);
140 term_chr('\n');
141 } else {
142 if (line)
143 ex_show(line);
147 static int vi_yankbuf(void)
149 int c = vi_read();
150 if (c == '"')
151 return vi_read();
152 vi_back(c);
153 return 0;
156 static int vi_prefix(void)
158 int n = 0;
159 int c = vi_read();
160 if ((c >= '1' && c <= '9')) {
161 while (isdigit(c)) {
162 n = n * 10 + c - '0';
163 c = vi_read();
166 vi_back(c);
167 return n;
170 static int vi_col2off(struct lbuf *lb, int row, int col)
172 char *ln = lbuf_get(lb, row);
173 return ln ? ren_off(ln, col) : 0;
176 static int vi_off2col(struct lbuf *lb, int row, int off)
178 char *ln = lbuf_get(lb, row);
179 return ln ? ren_pos(ln, off) : 0;
182 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
184 int o = *off + dir;
185 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
186 return 1;
187 *off = o;
188 return 0;
191 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
193 char *ln = lbuf_get(lb, *row);
194 int col = ln ? ren_pos(ln, *off) : 0;
195 int o = ln ? ren_next(ln, col, dir) : -1;
196 if (o < 0)
197 return -1;
198 *off = ren_off(ln, o);
199 return 0;
202 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
204 strcpy(vi_charlast, cs);
205 vi_charcmd = cmd;
206 return lbuf_findchar(lb, cs, cmd, n, row, off);
209 static int vi_search(int cmd, int cnt, int *row, int *off)
211 int r = *row;
212 int o = *off;
213 int failed = 0;
214 int len = 0;
215 int i, dir;
216 char *soff = "";
217 if (cmd == '/' || cmd == '?') {
218 char sign[4] = {cmd};
219 char *kw = vi_prompt(sign, ex_kmap());
220 if (!kw)
221 return 1;
222 xfinddir = cmd == '/' ? +1 : -1;
223 if (kw[0])
224 snprintf(xfindkwd, sizeof(xfindkwd), "%s", kw);
225 if (strchr(xfindkwd, cmd)) {
226 soff = strchr(xfindkwd, cmd) + 1;
227 *strchr(xfindkwd, cmd) = '\0';
229 free(kw);
231 dir = cmd == 'N' ? -xfinddir : xfinddir;
232 if (!xfindkwd[0] || !lbuf_len(xb))
233 return 1;
234 o = *off;
235 for (i = 0; i < cnt; i++) {
236 if (lbuf_search(xb, xfindkwd, dir, &r, &o, &len)) {
237 failed = 1;
238 break;
240 if (i + 1 < cnt && cmd == '/')
241 o += len;
243 if (!failed) {
244 *row = r;
245 *off = o;
246 while (soff[0] && isspace((unsigned char) soff[0]))
247 soff++;
248 if (soff[0]) {
249 *off = -1;
250 if (*row + atoi(soff) < 0 || *row + atoi(soff) >= lbuf_len(xb))
251 failed = 1;
252 else
253 *row += atoi(soff);
256 if (failed)
257 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", xfindkwd);
258 return failed;
261 /* read a line motion */
262 static int vi_motionln(int *row, int cmd)
264 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
265 int c = vi_read();
266 int mark, mark_row, mark_off;
267 switch (c) {
268 case '\n':
269 case '+':
270 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
271 break;
272 case '-':
273 *row = MAX(*row - cnt, 0);
274 break;
275 case '_':
276 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
277 break;
278 case '\'':
279 if ((mark = vi_read()) <= 0)
280 return -1;
281 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
282 return -1;
283 *row = mark_row;
284 break;
285 case 'j':
286 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
287 break;
288 case 'k':
289 *row = MAX(*row - cnt, 0);
290 break;
291 case 'G':
292 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
293 break;
294 case 'H':
295 if (lbuf_len(xb))
296 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
297 else
298 *row = 0;
299 break;
300 case 'L':
301 if (lbuf_len(xb))
302 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
303 else
304 *row = 0;
305 break;
306 case 'M':
307 if (lbuf_len(xb))
308 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
309 else
310 *row = 0;
311 break;
312 default:
313 if (c == cmd) {
314 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
315 break;
317 if (c == '%' && (vi_arg1 || vi_arg2)) {
318 if (cnt > 100)
319 return -1;
320 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
321 break;
323 vi_back(c);
324 return 0;
326 return c;
329 static char *vi_curword(struct lbuf *lb, int row, int off)
331 struct sbuf *sb;
332 char *ln = lbuf_get(lb, row);
333 char *beg, *end;
334 if (!ln)
335 return NULL;
336 beg = uc_chr(ln, ren_noeol(ln, off));
337 end = beg;
338 while (*end && uc_kind(end) == 1)
339 end = uc_next(end);
340 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
341 beg = uc_beg(ln, beg - 1);
342 if (beg >= end)
343 return NULL;
344 sb = sbuf_make();
345 sbuf_str(sb, "\\<");
346 sbuf_mem(sb, beg, end - beg);
347 sbuf_str(sb, "\\>");
348 return sbuf_done(sb);
351 /* read a motion */
352 static int vi_motion(int *row, int *off)
354 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
355 char *ln = lbuf_get(xb, *row);
356 int dir = dir_context(ln ? ln : "");
357 int mark, mark_row, mark_off;
358 char *cs;
359 int mv;
360 int i;
361 if ((mv = vi_motionln(row, 0))) {
362 *off = -1;
363 return mv;
365 mv = vi_read();
366 switch (mv) {
367 case 'f':
368 if (!(cs = vi_char()))
369 return -1;
370 if (vi_findchar(xb, cs, mv, cnt, row, off))
371 return -1;
372 break;
373 case 'F':
374 if (!(cs = vi_char()))
375 return -1;
376 if (vi_findchar(xb, cs, mv, cnt, row, off))
377 return -1;
378 break;
379 case ';':
380 if (!vi_charlast[0])
381 return -1;
382 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
383 return -1;
384 break;
385 case ',':
386 if (!vi_charlast[0])
387 return -1;
388 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
389 return -1;
390 break;
391 case 'h':
392 for (i = 0; i < cnt; i++)
393 if (vi_nextcol(xb, -1 * dir, row, off))
394 break;
395 break;
396 case 'l':
397 for (i = 0; i < cnt; i++)
398 if (vi_nextcol(xb, +1 * dir, row, off))
399 break;
400 break;
401 case 't':
402 if (!(cs = vi_char()))
403 return -1;
404 if (vi_findchar(xb, cs, mv, cnt, row, off))
405 return -1;
406 break;
407 case 'T':
408 if (!(cs = vi_char()))
409 return -1;
410 if (vi_findchar(xb, cs, mv, cnt, row, off))
411 return -1;
412 break;
413 case 'B':
414 for (i = 0; i < cnt; i++)
415 if (lbuf_wordend(xb, 1, -1, row, off))
416 break;
417 break;
418 case 'E':
419 for (i = 0; i < cnt; i++)
420 if (lbuf_wordend(xb, 1, +1, row, off))
421 break;
422 break;
423 case 'W':
424 for (i = 0; i < cnt; i++)
425 if (lbuf_wordbeg(xb, 1, +1, row, off))
426 break;
427 break;
428 case 'b':
429 for (i = 0; i < cnt; i++)
430 if (lbuf_wordend(xb, 0, -1, row, off))
431 break;
432 break;
433 case 'e':
434 for (i = 0; i < cnt; i++)
435 if (lbuf_wordend(xb, 0, +1, row, off))
436 break;
437 break;
438 case 'w':
439 for (i = 0; i < cnt; i++)
440 if (lbuf_wordbeg(xb, 0, +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 for (i = 0; i < cnt; i++)
450 if (lbuf_paragraphbeg(xb, +1, row, off))
451 break;
452 break;
453 case '[':
454 if (vi_read() != '[')
455 return -1;
456 for (i = 0; i < cnt; i++)
457 if (lbuf_sectionbeg(xb, -1, row, off))
458 break;
459 break;
460 case ']':
461 if (vi_read() != ']')
462 return -1;
463 for (i = 0; i < cnt; i++)
464 if (lbuf_sectionbeg(xb, +1, row, off))
465 break;
466 break;
467 case '0':
468 *off = 0;
469 break;
470 case '^':
471 *off = lbuf_indents(xb, *row);
472 break;
473 case '$':
474 *off = lbuf_eol(xb, *row);
475 break;
476 case '|':
477 *off = vi_col2off(xb, *row, cnt - 1);
478 vi_pcol = cnt - 1;
479 break;
480 case '/':
481 if (vi_search(mv, cnt, row, off))
482 return -1;
483 break;
484 case '?':
485 if (vi_search(mv, cnt, row, off))
486 return -1;
487 break;
488 case 'n':
489 if (vi_search(mv, cnt, row, off))
490 return -1;
491 break;
492 case 'N':
493 if (vi_search(mv, cnt, row, off))
494 return -1;
495 break;
496 case TK_CTL('a'):
497 if (!(cs = vi_curword(xb, *row, *off)))
498 return -1;
499 strcpy(xfindkwd, cs);
500 free(cs);
501 xfinddir = +1;
502 if (vi_search('n', cnt, row, off))
503 return -1;
504 break;
505 case ' ':
506 for (i = 0; i < cnt; i++)
507 if (vi_nextoff(xb, +1, row, off))
508 break;
509 break;
510 case 127:
511 case TK_CTL('h'):
512 for (i = 0; i < cnt; i++)
513 if (vi_nextoff(xb, -1, row, off))
514 break;
515 break;
516 case '`':
517 if ((mark = vi_read()) <= 0)
518 return -1;
519 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
520 return -1;
521 *row = mark_row;
522 *off = mark_off;
523 break;
524 case '%':
525 if (lbuf_pair(xb, row, off))
526 return -1;
527 break;
528 default:
529 vi_back(mv);
530 return 0;
532 return mv;
535 static void swap(int *a, int *b)
537 int t = *a;
538 *a = *b;
539 *b = t;
542 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
544 struct sbuf *sb;
545 char *s1, *s2, *s3;
546 if (r1 == r2)
547 return uc_sub(lbuf_get(lb, r1), o1, o2);
548 sb = sbuf_make();
549 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
550 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
551 s2 = lbuf_cp(lb, r1 + 1, r2);
552 sbuf_str(sb, s1);
553 sbuf_str(sb, s2);
554 sbuf_str(sb, s3);
555 free(s1);
556 free(s2);
557 free(s3);
558 return sbuf_done(sb);
561 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
563 char *region;
564 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
565 reg_put(vi_ybuf, region, lnmode);
566 free(region);
567 xrow = r1;
568 xoff = lnmode ? xoff : o1;
571 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
573 char *pref, *post;
574 char *region;
575 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
576 reg_put(vi_ybuf, region, lnmode);
577 free(region);
578 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
579 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
580 if (!lnmode) {
581 struct sbuf *sb = sbuf_make();
582 sbuf_str(sb, pref);
583 sbuf_str(sb, post);
584 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
585 sbuf_free(sb);
586 } else {
587 lbuf_edit(xb, NULL, r1, r2 + 1);
589 xrow = r1;
590 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
591 free(pref);
592 free(post);
595 static int linecount(char *s)
597 int n;
598 for (n = 0; s; n++)
599 if ((s = strchr(s, '\n')))
600 s++;
601 return n;
604 static int charcount(char *text, char *post)
606 int tlen = strlen(text);
607 int plen = strlen(post);
608 char *nl = text;
609 int i;
610 if (tlen < plen)
611 return 0;
612 for (i = 0; i < tlen - plen; i++)
613 if (text[i] == '\n')
614 nl = text + i + 1;
615 return uc_slen(nl) - uc_slen(post);
618 static char *vi_input(char *pref, char *post, int *row, int *off)
620 char *rep = led_input(pref, post, ex_kmap());
621 if (!rep)
622 return NULL;
623 *row = linecount(rep) - 1;
624 *off = charcount(rep, post) - 1;
625 if (*off < 0)
626 *off = 0;
627 return rep;
630 static char *vi_indents(char *ln)
632 struct sbuf *sb = sbuf_make();
633 while (xai && ln && (*ln == ' ' || *ln == '\t'))
634 sbuf_chr(sb, *ln++);
635 return sbuf_done(sb);
638 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
640 char *region;
641 int row, off;
642 char *rep;
643 char *pref, *post;
644 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
645 reg_put(vi_ybuf, region, lnmode);
646 free(region);
647 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
648 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
649 vi_drawrm(r1, r2, 0);
650 rep = vi_input(pref, post, &row, &off);
651 if (rep) {
652 lbuf_edit(xb, rep, r1, r2 + 1);
653 xrow = r1 + row - 1;
654 xoff = off;
655 free(rep);
657 free(pref);
658 free(post);
661 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
663 char *pref, *post;
664 char *region, *s;
665 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
666 s = region;
667 while (*s) {
668 int c = (unsigned char) s[0];
669 if (c <= 0x7f) {
670 if (cmd == 'u')
671 s[0] = tolower(c);
672 if (cmd == 'U')
673 s[0] = toupper(c);
674 if (cmd == '~')
675 s[0] = islower(c) ? toupper(c) : tolower(c);
677 s = uc_next(s);
679 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
680 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
681 if (!lnmode) {
682 struct sbuf *sb = sbuf_make();
683 sbuf_str(sb, pref);
684 sbuf_str(sb, region);
685 sbuf_str(sb, post);
686 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
687 sbuf_free(sb);
688 } else {
689 lbuf_edit(xb, region, r1, r2 + 1);
691 xrow = r2;
692 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
693 free(region);
694 free(pref);
695 free(post);
698 static void vi_pipe(int r1, int r2)
700 char *text;
701 char *rep;
702 char *kmap = NULL;
703 char *cmd = vi_prompt("!", &kmap);
704 if (!cmd)
705 return;
706 text = lbuf_cp(xb, r1, r2 + 1);
707 rep = cmd_pipe(cmd, text, 1, 1);
708 if (rep)
709 lbuf_edit(xb, rep, r1, r2 + 1);
710 free(cmd);
711 free(text);
712 free(rep);
715 static void vi_shift(int r1, int r2, int dir)
717 struct sbuf *sb;
718 char *ln;
719 int i;
720 for (i = r1; i <= r2; i++) {
721 if (!(ln = lbuf_get(xb, i)))
722 continue;
723 sb = sbuf_make();
724 if (dir > 0)
725 sbuf_chr(sb, '\t');
726 else
727 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
728 sbuf_str(sb, ln);
729 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
730 sbuf_free(sb);
732 xrow = r1;
733 xoff = lbuf_indents(xb, xrow);
736 static int vc_motion(int cmd)
738 int r1 = xrow, r2 = xrow; /* region rows */
739 int o1 = xoff, o2 = xoff; /* visual region columns */
740 int lnmode = 0; /* line-based region */
741 int mv;
742 vi_arg2 = vi_prefix();
743 if (vi_arg2 < 0)
744 return 1;
745 o1 = ren_noeol(lbuf_get(xb, r1), o1);
746 o2 = o1;
747 if ((mv = vi_motionln(&r2, cmd))) {
748 o2 = -1;
749 } else if (!(mv = vi_motion(&r2, &o2))) {
750 vi_read();
751 return 1;
753 if (mv < 0)
754 return 1;
755 lnmode = o2 < 0;
756 if (lnmode) {
757 o1 = 0;
758 o2 = lbuf_eol(xb, r2);
760 if (r1 > r2) {
761 swap(&r1, &r2);
762 swap(&o1, &o2);
764 if (r1 == r2 && o1 > o2)
765 swap(&o1, &o2);
766 o1 = ren_noeol(lbuf_get(xb, r1), o1);
767 if (!lnmode && strchr("fFtTeE%", mv))
768 if (o2 < lbuf_eol(xb, r2))
769 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
770 if (cmd == 'y')
771 vi_yank(r1, o1, r2, o2, lnmode);
772 if (cmd == 'd')
773 vi_delete(r1, o1, r2, o2, lnmode);
774 if (cmd == 'c')
775 vi_change(r1, o1, r2, o2, lnmode);
776 if (cmd == '~' || cmd == 'u' || cmd == 'U')
777 vi_case(r1, o1, r2, o2, lnmode, cmd);
778 if (cmd == '!')
779 vi_pipe(r1, r2);
780 if (cmd == '>' || cmd == '<')
781 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
782 return 0;
785 static int vc_insert(int cmd)
787 char *pref, *post;
788 char *ln = lbuf_get(xb, xrow);
789 int row, off = 0;
790 char *rep;
791 if (cmd == 'I')
792 xoff = lbuf_indents(xb, xrow);
793 if (cmd == 'A')
794 xoff = lbuf_eol(xb, xrow);
795 xoff = ren_noeol(ln, xoff);
796 if (cmd == 'o')
797 xrow += 1;
798 if (cmd == 'i' || cmd == 'I')
799 off = xoff;
800 if (cmd == 'a' || cmd == 'A')
801 off = xoff + 1;
802 if (ln && ln[0] == '\n')
803 off = 0;
804 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
805 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
806 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
807 rep = vi_input(pref, post, &row, &off);
808 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
809 lbuf_edit(xb, "\n", 0, 0);
810 if (rep) {
811 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
812 xrow += row - 1;
813 xoff = off;
814 free(rep);
816 free(pref);
817 free(post);
818 return !rep;
821 static int vc_put(int cmd)
823 int cnt = MAX(1, vi_arg1);
824 int lnmode;
825 char *buf = reg_get(vi_ybuf, &lnmode);
826 int i;
827 if (!buf) {
828 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
829 return 1;
831 if (lnmode) {
832 struct sbuf *sb = sbuf_make();
833 for (i = 0; i < cnt; i++)
834 sbuf_str(sb, buf);
835 if (!lbuf_len(xb))
836 lbuf_edit(xb, "\n", 0, 0);
837 if (cmd == 'p')
838 xrow++;
839 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
840 xoff = lbuf_indents(xb, xrow);
841 sbuf_free(sb);
842 } else {
843 struct sbuf *sb = sbuf_make();
844 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
845 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
846 char *s = uc_sub(ln, 0, off);
847 sbuf_str(sb, s);
848 free(s);
849 for (i = 0; i < cnt; i++)
850 sbuf_str(sb, buf);
851 s = uc_sub(ln, off, -1);
852 sbuf_str(sb, s);
853 free(s);
854 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
855 xoff = off + uc_slen(buf) * cnt - 1;
856 sbuf_free(sb);
858 return 0;
861 static int join_spaces(char *prev, char *next)
863 int prevlen = strlen(prev);
864 if (!prev[0])
865 return 0;
866 if (prev[prevlen - 1] == ' ' || next[0] == ')')
867 return 0;
868 return prev[prevlen - 1] == '.' ? 2 : 1;
871 static int vc_join(void)
873 struct sbuf *sb;
874 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
875 int beg = xrow;
876 int end = xrow + cnt;
877 int off = 0;
878 int i;
879 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
880 return 1;
881 sb = sbuf_make();
882 for (i = beg; i < end; i++) {
883 char *ln = lbuf_get(xb, i);
884 char *lnend = strchr(ln, '\n');
885 int spaces;
886 if (i > beg)
887 while (ln[0] == ' ' || ln[0] == '\t')
888 ln++;
889 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
890 off = uc_slen(sbuf_buf(sb));
891 while (spaces--)
892 sbuf_chr(sb, ' ');
893 sbuf_mem(sb, ln, lnend - ln);
895 sbuf_chr(sb, '\n');
896 lbuf_edit(xb, sbuf_buf(sb), beg, end);
897 xoff = off;
898 sbuf_free(sb);
899 return 0;
902 static int vi_scrollforeward(int cnt)
904 if (xtop >= lbuf_len(xb) - 1)
905 return 1;
906 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
907 xrow = MAX(xrow, xtop);
908 return 0;
911 static int vi_scrollbackward(int cnt)
913 if (xtop == 0)
914 return 1;
915 xtop = MAX(0, xtop - cnt);
916 xrow = MIN(xrow, xtop + xrows - 1);
917 return 0;
920 static void vc_status(void)
922 int col = vi_off2col(xb, xrow, xoff);
923 snprintf(vi_msg, sizeof(vi_msg),
924 "\"%s\"%c %d lines L%d C%d\n",
925 ex_path()[0] ? ex_path() : "unnamed",
926 lbuf_modified(xb) ? '*' : ' ',
927 lbuf_len(xb), xrow + 1,
928 ren_cursor(lbuf_get(xb, xrow), col) + 1);
931 static int vc_replace(void)
933 int cnt = MAX(1, vi_arg1);
934 char *cs = vi_char();
935 char *ln = lbuf_get(xb, xrow);
936 struct sbuf *sb;
937 char *pref, *post;
938 char *s;
939 int off, i;
940 if (!ln || !cs)
941 return 1;
942 off = ren_noeol(ln, xoff);
943 s = uc_chr(ln, off);
944 for (i = 0; s[0] != '\n' && i < cnt; i++)
945 s = uc_next(s);
946 if (i < cnt)
947 return 1;
948 pref = uc_sub(ln, 0, off);
949 post = uc_sub(ln, off + cnt, -1);
950 sb = sbuf_make();
951 sbuf_str(sb, pref);
952 for (i = 0; i < cnt; i++)
953 sbuf_str(sb, cs);
954 sbuf_str(sb, post);
955 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
956 off += cnt - 1;
957 xoff = off;
958 sbuf_free(sb);
959 free(pref);
960 free(post);
961 return 0;
964 static char rep_cmd[4096]; /* the last command */
965 static int rep_len;
967 static void vc_repeat(void)
969 term_push(rep_cmd, rep_len);
972 static void vc_execute(void)
974 static int exec_buf;
975 int lnmode;
976 int c = vi_read();
977 char *buf;
978 if (TK_INT(c))
979 return;
980 if (c == '@')
981 c = exec_buf;
982 exec_buf = c;
983 buf = reg_get(exec_buf, &lnmode);
984 if (buf)
985 term_push(buf, strlen(buf));
988 static void vi(void)
990 int xcol;
991 int mark;
992 char *ln;
993 char *kmap = NULL;
994 xtop = MAX(0, xrow - xrows / 2);
995 xoff = 0;
996 xcol = vi_off2col(xb, xrow, xoff);
997 vi_draw(xcol);
998 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
999 while (!xquit) {
1000 int redraw = 0;
1001 int nrow = xrow;
1002 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1003 int otop = xtop;
1004 int oleft = xleft;
1005 int mv, n;
1006 term_cmd(&n);
1007 vi_arg2 = 0;
1008 vi_ybuf = vi_yankbuf();
1009 vi_arg1 = vi_prefix();
1010 if (!vi_ybuf)
1011 vi_ybuf = vi_yankbuf();
1012 mv = vi_motion(&nrow, &noff);
1013 if (mv > 0) {
1014 if (strchr("\'`GHML/?{}[]nN", mv) ||
1015 (mv == '%' && noff < 0)) {
1016 lbuf_mark(xb, '\'', xrow, xoff);
1017 lbuf_mark(xb, '`', xrow, xoff);
1019 xrow = nrow;
1020 if (noff < 0 && !strchr("jk", mv))
1021 noff = lbuf_indents(xb, xrow);
1022 if (strchr("jk", mv))
1023 noff = vi_col2off(xb, xrow, xcol);
1024 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1025 if (!strchr("|jk", mv))
1026 xcol = vi_off2col(xb, xrow, xoff);
1027 if (mv == '|')
1028 xcol = vi_pcol;
1029 } else if (mv == 0) {
1030 char *cmd;
1031 int c = vi_read();
1032 int k;
1033 if (c <= 0)
1034 continue;
1035 lbuf_mark(xb, '*', xrow, xoff);
1036 switch (c) {
1037 case TK_CTL('b'):
1038 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1039 break;
1040 xoff = lbuf_indents(xb, xrow);
1041 redraw = 1;
1042 break;
1043 case TK_CTL('f'):
1044 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1045 break;
1046 xoff = lbuf_indents(xb, xrow);
1047 redraw = 1;
1048 break;
1049 case TK_CTL('e'):
1050 if (vi_scrollforeward(MAX(1, vi_arg1)))
1051 break;
1052 redraw = 1;
1053 break;
1054 case TK_CTL('y'):
1055 if (vi_scrollbackward(MAX(1, vi_arg1)))
1056 break;
1057 redraw = 1;
1058 break;
1059 case TK_CTL('u'):
1060 if (xrow == 0)
1061 break;
1062 if (vi_arg1)
1063 vi_scroll = vi_arg1;
1064 n = vi_scroll ? vi_scroll : xrows / 2;
1065 xrow = MAX(0, xrow - n);
1066 if (xtop > 0)
1067 xtop = MAX(0, xtop - n);
1068 redraw = 1;
1069 xoff = lbuf_indents(xb, xrow);
1070 break;
1071 case TK_CTL('d'):
1072 if (xrow == lbuf_len(xb) - 1)
1073 break;
1074 if (vi_arg1)
1075 vi_scroll = vi_arg1;
1076 n = vi_scroll ? vi_scroll : xrows / 2;
1077 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1078 if (xtop < lbuf_len(xb) - xrows)
1079 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1080 redraw = 1;
1081 xoff = lbuf_indents(xb, xrow);
1082 break;
1083 case TK_CTL('z'):
1084 term_pos(xrows, 0);
1085 term_suspend();
1086 redraw = 1;
1087 break;
1088 case 'u':
1089 if (!lbuf_undo(xb)) {
1090 lbuf_jump(xb, '*', &xrow, &xoff);
1091 redraw = 1;
1092 } else {
1093 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1095 break;
1096 case TK_CTL('r'):
1097 if (!lbuf_redo(xb)) {
1098 lbuf_jump(xb, '*', &xrow, &xoff);
1099 redraw = 1;
1100 } else {
1101 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1103 break;
1104 case TK_CTL('g'):
1105 vc_status();
1106 break;
1107 case TK_CTL('^'):
1108 ex_command("e #");
1109 redraw = 1;
1110 break;
1111 case ':':
1112 ln = vi_prompt(":", &kmap);
1113 if (ln && ln[0]) {
1114 ex_command(ln);
1115 redraw = 1;
1117 free(ln);
1118 if (xquit)
1119 continue;
1120 break;
1121 case 'c':
1122 case 'd':
1123 case 'y':
1124 case '!':
1125 case '>':
1126 case '<':
1127 if (!vc_motion(c))
1128 redraw = 1;
1129 break;
1130 case 'i':
1131 case 'I':
1132 case 'a':
1133 case 'A':
1134 case 'o':
1135 case 'O':
1136 if (!vc_insert(c))
1137 redraw = 1;
1138 break;
1139 case 'J':
1140 if (!vc_join())
1141 redraw = 1;
1142 break;
1143 case TK_CTL('l'):
1144 redraw = 1;
1145 break;
1146 case 'm':
1147 if ((mark = vi_read()) > 0 && islower(mark))
1148 lbuf_mark(xb, mark, xrow, xoff);
1149 break;
1150 case 'p':
1151 case 'P':
1152 if (!vc_put(c))
1153 redraw = 1;
1154 break;
1155 case 'z':
1156 k = vi_read();
1157 switch (k) {
1158 case '\n':
1159 xtop = vi_arg1 ? vi_arg1 : xrow;
1160 break;
1161 case '.':
1162 n = vi_arg1 ? vi_arg1 : xrow;
1163 xtop = MAX(0, n - xrows / 2);
1164 break;
1165 case '-':
1166 n = vi_arg1 ? vi_arg1 : xrow;
1167 xtop = MAX(0, n - xrows + 1);
1168 break;
1169 case 'l':
1170 case 'r':
1171 xdir = k == 'r' ? -1 : +1;
1172 break;
1173 case 'L':
1174 case 'R':
1175 xdir = k == 'R' ? -2 : +2;
1176 break;
1178 redraw = 1;
1179 break;
1180 case 'g':
1181 k = vi_read();
1182 if (k == '~' || k == 'u' || k == 'U')
1183 if (!vc_motion(k))
1184 redraw = 1;
1185 break;
1186 case 'x':
1187 vi_back(' ');
1188 if (!vc_motion('d'))
1189 redraw = 1;
1190 break;
1191 case 'X':
1192 vi_back(TK_CTL('h'));
1193 if (!vc_motion('d'))
1194 redraw = 1;
1195 break;
1196 case 'C':
1197 vi_back('$');
1198 if (!vc_motion('c'))
1199 redraw = 1;
1200 break;
1201 case 'D':
1202 vi_back('$');
1203 if (!vc_motion('d'))
1204 redraw = 1;
1205 break;
1206 case 'r':
1207 if (!vc_replace())
1208 redraw = 1;
1209 break;
1210 case 's':
1211 vi_back(' ');
1212 if (!vc_motion('c'))
1213 redraw = 1;
1214 break;
1215 case 'S':
1216 vi_back('c');
1217 if (!vc_motion('c'))
1218 redraw = 1;
1219 break;
1220 case 'Y':
1221 vi_back('y');
1222 if (!vc_motion('y'))
1223 redraw = 1;
1224 break;
1225 case 'Z':
1226 k = vi_read();
1227 if (k == 'Z')
1228 ex_command("x");
1229 break;
1230 case '~':
1231 vi_back(' ');
1232 if (!vc_motion('~'))
1233 redraw = 1;
1234 break;
1235 case '.':
1236 vc_repeat();
1237 break;
1238 case '@':
1239 vc_execute();
1240 break;
1241 default:
1242 continue;
1244 cmd = term_cmd(&n);
1245 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1246 if (n < sizeof(rep_cmd)) {
1247 memcpy(rep_cmd, cmd, n);
1248 rep_len = n;
1252 if (xrow < 0 || xrow >= lbuf_len(xb))
1253 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1254 if (xtop > xrow)
1255 xtop = xtop - xrows / 2 > xrow ?
1256 MAX(0, xrow - xrows / 2) : xrow;
1257 if (xtop + xrows <= xrow)
1258 xtop = xtop + xrows + xrows / 2 <= xrow ?
1259 xrow - xrows / 2 : xrow - xrows + 1;
1260 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1261 if (redraw)
1262 xcol = vi_off2col(xb, xrow, xoff);
1263 if (xcol >= xleft + xcols)
1264 xleft = xcol - xcols / 2;
1265 if (xcol < xleft)
1266 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1267 vi_wait();
1268 if (redraw || xtop != otop || xleft != oleft)
1269 vi_draw(xcol);
1270 if (vi_msg[0])
1271 vi_drawmsg();
1272 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1273 ren_cursor(lbuf_get(xb, xrow), xcol)));
1274 lbuf_modified(xb);
1276 term_pos(xrows, 0);
1277 term_kill();
1280 int main(int argc, char *argv[])
1282 int i;
1283 xvis = 1;
1284 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1285 if (argv[i][1] == 's')
1286 xled = 0;
1287 if (argv[i][1] == 'e')
1288 xvis = 0;
1289 if (argv[i][1] == 'v')
1290 xvis = 1;
1292 dir_init();
1293 syn_init();
1294 if (xled || xvis)
1295 term_init();
1296 if (!ex_init(argv + i)) {
1297 if (xvis)
1298 vi();
1299 else
1300 ex();
1301 if (xled || xvis)
1302 term_done();
1303 ex_done();
1305 reg_done();
1306 syn_done();
1307 dir_done();
1308 return 0;