ex: show a message after :e, :r, :w, or failed searches
[neatvi.git] / vi.c
blobb358e1b4df9b3c04899114b620d9fb35a6ca85ee
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 char xpath[PATHLEN]; /* current file */
16 char xpath_alt[PATHLEN]; /* alternate file */
17 char xmsg[512]; /* current message */
18 struct lbuf *xb; /* current buffer */
19 int xrow, xcol, xtop; /* current row, column, and top row */
20 int xrow_alt; /* alternate row, column, and top row */
21 int xled = 1; /* use the line editor */
22 int xdir = 'L'; /* current direction context */
23 int xvis; /* visual mode */
24 int xquit;
25 int xautoindent = 1;
26 static char vi_findlast[256]; /* the last searched keyword */
27 static int vi_finddir; /* the last search direction */
28 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
29 static int vi_charcmd; /* the character finding command */
30 static int vi_arg1, vi_arg2; /* the first and second arguments */
31 static int vi_ybuf; /* current yank buffer */
33 static void vi_drawmsg(void)
35 led_print(xmsg, xrows);
36 xmsg[0] = '\0';
39 static void vi_draw(void)
41 int i;
42 term_record();
43 for (i = xtop; i < xtop + xrows; i++) {
44 char *s = lbuf_get(xb, i);
45 led_print(s ? s : "~", i - xtop);
47 vi_drawmsg();
48 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
49 term_commit();
52 static int vi_buf[128];
53 static int vi_buflen;
55 static int vi_read(void)
57 return vi_buflen ? vi_buf[--vi_buflen] : term_read(1000);
60 static void vi_back(int c)
62 if (vi_buflen < sizeof(vi_buf))
63 vi_buf[vi_buflen++] = c;
66 static char *vi_char(void)
68 int key = vi_read();
69 return TK_INT(key) ? NULL : led_keymap(key);
72 static char *vi_prompt(char *msg)
74 term_pos(xrows, led_pos(msg, 0));
75 term_kill();
76 return led_prompt(msg, "");
79 char *ex_read(char *msg)
81 struct sbuf *sb;
82 char c;
83 if (xled) {
84 char *s = led_prompt(msg, "");
85 if (s)
86 term_chr('\n');
87 return s;
89 sb = sbuf_make();
90 while ((c = getchar()) != EOF && c != '\n')
91 sbuf_chr(sb, c);
92 if (c == EOF) {
93 sbuf_free(sb);
94 return NULL;
96 return sbuf_done(sb);
99 void ex_show(char *msg)
101 if (xvis) {
102 snprintf(xmsg, sizeof(xmsg), "%s", msg);
103 } else if (xled) {
104 led_print(msg, -1);
105 term_chr('\n');
106 } else {
107 printf("%s", msg);
111 static int vi_yankbuf(void)
113 int c = vi_read();
114 if (c == '"')
115 return vi_read();
116 vi_back(c);
117 return 0;
120 static int vi_prefix(void)
122 int n = 0;
123 int c = vi_read();
124 if ((c >= '1' && c <= '9')) {
125 while (isdigit(c)) {
126 n = n * 10 + c - '0';
127 c = vi_read();
130 vi_back(c);
131 return n;
134 static int lbuf_lnnext(struct lbuf *lb, int *r, int *c, int dir)
136 char *ln = lbuf_get(lb, *r);
137 int col = ln ? ren_next(ln, *c, dir) : -1;
138 if (col < 0)
139 return -1;
140 *c = col;
141 return 0;
144 static void lbuf_eol(struct lbuf *lb, int *r, int *c, int dir)
146 char *ln = lbuf_get(lb, *r);
147 *c = dir < 0 ? 0 : MAX(0, ren_wid(ln ? ln : "") - 1);
150 static int lbuf_next(struct lbuf *lb, int *r, int *c, int dir)
152 if (dir < 0 && *r >= lbuf_len(lb))
153 *r = MAX(0, lbuf_len(lb) - 1);
154 if (lbuf_lnnext(lb, r, c, dir)) {
155 if (!lbuf_get(lb, *r + dir))
156 return -1;
157 *r += dir;
158 lbuf_eol(lb, r, c, -dir);
159 return 0;
161 return 0;
164 /* return a pointer to the character at visual position c of line r */
165 static char *lbuf_chr(struct lbuf *lb, int r, int c)
167 char *ln = lbuf_get(lb, r);
168 return ln ? uc_chr(ln, ren_off(ln, c)) : "";
171 static void lbuf_postindents(struct lbuf *lb, int *r, int *c)
173 lbuf_eol(lb, r, c, -1);
174 while (uc_isspace(lbuf_chr(lb, *r, *c)))
175 if (lbuf_lnnext(lb, r, c, +1))
176 break;
179 static int lbuf_findchar(struct lbuf *lb, int *row, int *col, char *cs, int cmd, int n)
181 int dir = (cmd == 'f' || cmd == 't') ? +1 : -1;
182 int c = *col;
183 if (n < 0)
184 dir = -dir;
185 if (n < 0)
186 n = -n;
187 strcpy(vi_charlast, cs);
188 vi_charcmd = cmd;
189 while (n > 0 && !lbuf_lnnext(lb, row, &c, dir))
190 if (uc_code(lbuf_chr(lb, *row, c)) == uc_code(cs))
191 n--;
192 if (!n)
193 *col = c;
194 if (!n && (cmd == 't' || cmd == 'T'))
195 lbuf_lnnext(lb, row, col, -dir);
196 return n != 0;
199 static int lbuf_search(struct lbuf *lb, char *kw, int dir, int *r, int *c, int *len)
201 int offs[2];
202 int found = 0;
203 int row = *r, col = *c;
204 int i;
205 struct rset *re = rset_make(1, &kw, 0);
206 if (!re)
207 return 1;
208 for (i = row; !found && i >= 0 && i < lbuf_len(lb); i += dir) {
209 char *s = lbuf_get(lb, i);
210 int off = dir > 0 && row == i ? uc_chr(s, col + 1) - s : 0;
211 int flg = off ? RE_NOTBOL : 0;
212 while (rset_find(re, s + off, 1, offs, flg) >= 0) {
213 if (dir < 0 && row == i && off + offs[0] >= col)
214 break;
215 found = 1;
216 *c = uc_off(s, off + offs[0]);
217 *r = i;
218 *len = offs[1] - offs[0];
219 off += offs[1];
220 if (dir > 0)
221 break;
224 rset_free(re);
225 return !found;
228 static int vi_search(int cmd, int cnt, int *row, int *col)
230 int r = *row;
231 int c = *col;
232 int failed = 0;
233 int len = 0;
234 int i, dir;
235 char *off = "";
236 if (cmd == '/' || cmd == '?') {
237 char sign[4] = {cmd};
238 char *kw = vi_prompt(sign);
239 if (!kw)
240 return 1;
241 vi_finddir = cmd == '/' ? +1 : -1;
242 if (kw[0])
243 snprintf(vi_findlast, sizeof(vi_findlast), "%s", kw);
244 if (strchr(vi_findlast, cmd)) {
245 off = strchr(vi_findlast, cmd) + 1;
246 *strchr(vi_findlast, cmd) = '\0';
248 free(kw);
250 dir = cmd == 'N' ? -vi_finddir : vi_finddir;
251 if (!vi_findlast[0] || !lbuf_len(xb))
252 return 1;
253 c = ren_off(lbuf_get(xb, *row), *col);
254 for (i = 0; i < cnt; i++) {
255 if (lbuf_search(xb, vi_findlast, dir, &r, &c, &len)) {
256 failed = 1;
257 break;
259 if (i + 1 < cnt && cmd == '/')
260 c += len;
262 if (!failed) {
263 *row = r;
264 *col = ren_pos(lbuf_get(xb, r), c);
265 while (off[0] && isspace((unsigned char) off[0]))
266 off++;
267 if (off[0]) {
268 *col = -1;
269 if (*row + atoi(off) < 0 || *row + atoi(off) >= lbuf_len(xb))
270 failed = 1;
271 else
272 *row += atoi(off);
275 if (failed)
276 snprintf(xmsg, sizeof(xmsg), "\"%s\" not found\n", vi_findlast);
277 return failed;
280 /* move to the last character of the word */
281 static int lbuf_wordlast(struct lbuf *lb, int *row, int *col, int kind, int dir)
283 if (!kind || !(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
284 return 0;
285 while (uc_kind(lbuf_chr(lb, *row, *col)) & kind)
286 if (lbuf_next(lb, row, col, dir))
287 return 1;
288 if (!(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
289 lbuf_next(lb, row, col, -dir);
290 return 0;
293 static int lbuf_wordbeg(struct lbuf *lb, int *row, int *col, int big, int dir)
295 int nl = 0;
296 lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir);
297 if (lbuf_next(lb, row, col, dir))
298 return 1;
299 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
300 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
301 if (nl == 2)
302 return 0;
303 if (lbuf_next(lb, row, col, dir))
304 return 1;
306 return 0;
309 static int lbuf_wordend(struct lbuf *lb, int *row, int *col, int big, int dir)
311 int nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? -1 : 0;
312 if (!uc_isspace(lbuf_chr(lb, *row, *col)))
313 if (lbuf_next(lb, row, col, dir))
314 return 1;
315 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
316 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
317 if (nl == 2) {
318 if (dir < 0)
319 lbuf_next(lb, row, col, -dir);
320 return 0;
322 if (lbuf_next(lb, row, col, dir))
323 return 1;
325 if (lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir))
326 return 1;
327 return 0;
330 static int lbuf_paragraphbeg(struct lbuf *lb, int *row, int *col, int dir)
332 while (*row >= 0 && *row < lbuf_len(lb) && !strcmp("\n", lbuf_get(lb, *row)))
333 *row += dir;
334 while (*row >= 0 && *row < lbuf_len(lb) && strcmp("\n", lbuf_get(lb, *row)))
335 *row += dir;
336 *row = MAX(0, MIN(*row, lbuf_len(lb) - 1));
337 lbuf_eol(lb, row, col, -1);
338 return 0;
341 static int lbuf_sectionbeg(struct lbuf *lb, int *row, int *col, int dir)
343 *row += dir;
344 while (*row >= 0 && *row < lbuf_len(lb) && lbuf_get(lb, *row)[0] != '{')
345 *row += dir;
346 *row = MAX(0, MIN(*row, lbuf_len(lb) - 1));
347 lbuf_eol(lb, row, col, -1);
348 return 0;
351 /* read a line motion */
352 static int vi_motionln(int *row, int cmd)
354 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
355 int c = vi_read();
356 int mark;
357 switch (c) {
358 case '\n':
359 case '+':
360 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
361 break;
362 case '-':
363 *row = MAX(*row - cnt, 0);
364 break;
365 case '_':
366 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
367 break;
368 case '\'':
369 if ((mark = vi_read()) > 0 && (isalpha(mark) || mark == '\''))
370 if (lbuf_markpos(xb, mark) >= 0)
371 *row = lbuf_markpos(xb, mark);
372 break;
373 case 'j':
374 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
375 break;
376 case 'k':
377 *row = MAX(*row - cnt, 0);
378 break;
379 case 'G':
380 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
381 break;
382 case 'H':
383 if (lbuf_len(xb))
384 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
385 else
386 *row = 0;
387 break;
388 case 'L':
389 if (lbuf_len(xb))
390 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
391 else
392 *row = 0;
393 break;
394 case 'M':
395 if (lbuf_len(xb))
396 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
397 else
398 *row = 0;
399 break;
400 default:
401 if (c == cmd) {
402 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
403 break;
405 vi_back(c);
406 return 0;
408 return c;
411 /* read a motion */
412 static int vi_motion(int *row, int *col)
414 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
415 char *ln = lbuf_get(xb, *row);
416 int dir = dir_context(ln ? ln : "");
417 char *cs;
418 int mv;
419 int i;
420 if ((mv = vi_motionln(row, 0))) {
421 *col = -1;
422 return mv;
424 mv = vi_read();
425 switch (mv) {
426 case ' ':
427 for (i = 0; i < cnt; i++)
428 if (lbuf_lnnext(xb, row, col, 1))
429 break;
430 break;
431 case 'f':
432 if ((cs = vi_char()))
433 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
434 return -1;
435 break;
436 case 'F':
437 if ((cs = vi_char()))
438 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
439 return -1;
440 break;
441 case ';':
442 if (vi_charlast[0])
443 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, cnt))
444 return -1;
445 break;
446 case ',':
447 if (vi_charlast[0])
448 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, -cnt))
449 return -1;
450 break;
451 case 'h':
452 for (i = 0; i < cnt; i++)
453 if (lbuf_lnnext(xb, row, col, -1 * dir))
454 break;
455 break;
456 case 'l':
457 for (i = 0; i < cnt; i++)
458 if (lbuf_lnnext(xb, row, col, +1 * dir))
459 break;
460 break;
461 case 't':
462 if ((cs = vi_char()))
463 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
464 return -1;
465 break;
466 case 'T':
467 if ((cs = vi_char()))
468 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
469 return -1;
470 break;
471 case 'B':
472 for (i = 0; i < cnt; i++)
473 if (lbuf_wordend(xb, row, col, 1, -1))
474 break;
475 break;
476 case 'E':
477 for (i = 0; i < cnt; i++)
478 if (lbuf_wordend(xb, row, col, 1, +1))
479 break;
480 break;
481 case 'W':
482 for (i = 0; i < cnt; i++)
483 if (lbuf_wordbeg(xb, row, col, 1, +1))
484 break;
485 break;
486 case 'b':
487 for (i = 0; i < cnt; i++)
488 if (lbuf_wordend(xb, row, col, 0, -1))
489 break;
490 break;
491 case 'e':
492 for (i = 0; i < cnt; i++)
493 if (lbuf_wordend(xb, row, col, 0, +1))
494 break;
495 break;
496 case 'w':
497 for (i = 0; i < cnt; i++)
498 if (lbuf_wordbeg(xb, row, col, 0, +1))
499 break;
500 break;
501 case '{':
502 for (i = 0; i < cnt; i++)
503 if (lbuf_paragraphbeg(xb, row, col, -1))
504 break;
505 break;
506 case '}':
507 for (i = 0; i < cnt; i++)
508 if (lbuf_paragraphbeg(xb, row, col, +1))
509 break;
510 break;
511 case '[':
512 if (vi_read() != '[')
513 return -1;
514 for (i = 0; i < cnt; i++)
515 if (lbuf_sectionbeg(xb, row, col, -1))
516 break;
517 break;
518 case ']':
519 if (vi_read() != ']')
520 return -1;
521 for (i = 0; i < cnt; i++)
522 if (lbuf_sectionbeg(xb, row, col, +1))
523 break;
524 break;
525 case '0':
526 lbuf_eol(xb, row, col, -1);
527 break;
528 case '^':
529 lbuf_postindents(xb, row, col);
530 break;
531 case '$':
532 lbuf_eol(xb, row, col, +1);
533 lbuf_lnnext(xb, row, col, -1);
534 break;
535 case '|':
536 *col = cnt - 1;
537 break;
538 case '/':
539 if (vi_search(mv, cnt, row, col))
540 return -1;
541 break;
542 case '?':
543 if (vi_search(mv, cnt, row, col))
544 return -1;
545 break;
546 case 'n':
547 if (vi_search(mv, cnt, row, col))
548 return -1;
549 break;
550 case 'N':
551 if (vi_search(mv, cnt, row, col))
552 return -1;
553 break;
554 case 127:
555 case TK_CTL('h'):
556 for (i = 0; i < cnt; i++)
557 if (lbuf_lnnext(xb, row, col, -1))
558 break;
559 break;
560 default:
561 vi_back(mv);
562 return 0;
564 return mv;
567 static void swap(int *a, int *b)
569 int t = *a;
570 *a = *b;
571 *b = t;
574 static char *lbuf_region(struct lbuf *lb, int r1, int l1, int r2, int l2)
576 struct sbuf *sb;
577 char *s1, *s2, *s3;
578 if (r1 == r2)
579 return uc_sub(lbuf_get(lb, r1), l1, l2);
580 sb = sbuf_make();
581 s1 = uc_sub(lbuf_get(lb, r1), l1, -1);
582 s3 = uc_sub(lbuf_get(lb, r2), 0, l2);
583 s2 = lbuf_cp(lb, r1 + 1, r2);
584 sbuf_str(sb, s1);
585 sbuf_str(sb, s2);
586 sbuf_str(sb, s3);
587 free(s1);
588 free(s2);
589 free(s3);
590 return sbuf_done(sb);
593 /* insertion offset before or after the given visual position */
594 static int vi_insertionoffset(char *s, int c1, int before)
596 int l;
597 if (!s || !*s)
598 return 0;
599 l = ren_off(s, c1);
600 return before || s[l] == '\n' ? l : l + 1;
603 static void vi_commandregion(int *r1, int *r2, int *c1, int *c2, int *l1, int *l2, int closed)
605 if (*r2 < *r1 || (*r2 == *r1 && *c2 < *c1)) {
606 swap(r1, r2);
607 swap(c1, c2);
609 *l1 = vi_insertionoffset(lbuf_get(xb, *r1), *c1, 1);
610 *l2 = vi_insertionoffset(lbuf_get(xb, *r2), *c2, !closed);
611 if (*r1 == *r2 && lbuf_get(xb, *r1))
612 ren_region(lbuf_get(xb, *r1), *c1, *c2, l1, l2, closed);
613 if (*r1 == *r2 && *l2 < *l1)
614 swap(l1, l2);
617 static void vi_yank(int r1, int c1, int r2, int c2, int lnmode, int closed)
619 char *region;
620 int l1, l2;
621 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
622 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
623 reg_put(vi_ybuf, region, lnmode);
624 free(region);
625 xrow = r1;
626 xcol = lnmode ? xcol : c1;
629 static void vi_delete(int r1, int c1, int r2, int c2, int lnmode, int closed)
631 char *pref, *post;
632 char *region;
633 int l1, l2;
634 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
635 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
636 reg_put(vi_ybuf, region, lnmode);
637 free(region);
638 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, l1);
639 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
640 lbuf_rm(xb, r1, r2 + 1);
641 if (!lnmode) {
642 struct sbuf *sb = sbuf_make();
643 sbuf_str(sb, pref);
644 sbuf_str(sb, post);
645 lbuf_put(xb, r1, sbuf_buf(sb));
646 sbuf_free(sb);
648 xrow = r1;
649 xcol = c1;
650 if (lnmode)
651 lbuf_postindents(xb, &xrow, &xcol);
652 free(pref);
653 free(post);
656 static int lastline(char *str)
658 char *s = str;
659 char *r = s;
660 while (s && s[0]) {
661 r = s;
662 s = strchr(s == str ? s : s + 1, '\n');
664 return r - str;
667 static int linecount(char *s)
669 int n;
670 for (n = 0; s; n++)
671 if ((s = strchr(s, '\n')))
672 s++;
673 return n;
676 static int indentscopy(char *d, char *s, int len)
678 int i;
679 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
680 d[i] = s[i];
681 d[i] = '\0';
682 return i;
685 static char *vi_input(char *pref, char *post, int *row, int *col)
687 char ai[64] = "";
688 char *rep;
689 struct sbuf *sb;
690 int last, off;
691 if (xautoindent)
692 pref += indentscopy(ai, pref, sizeof(ai));
693 rep = led_input(pref, post, ai, xautoindent ? sizeof(ai) - 1 : 0);
694 if (!rep)
695 return NULL;
696 sb = sbuf_make();
697 sbuf_str(sb, ai);
698 sbuf_str(sb, pref);
699 sbuf_str(sb, rep);
700 last = lastline(sbuf_buf(sb));
701 off = uc_slen(sbuf_buf(sb) + last);
702 if (last)
703 while (xautoindent && (post[0] == ' ' || post[0] == '\t'))
704 post++;
705 sbuf_str(sb, post);
706 *row = linecount(sbuf_buf(sb)) - 1;
707 *col = ren_pos(sbuf_buf(sb) + last, MAX(0, off - 1));
708 free(rep);
709 return sbuf_done(sb);
712 static char *vi_indents(char *ln)
714 struct sbuf *sb = sbuf_make();
715 while (xautoindent && ln && (*ln == ' ' || *ln == '\t'))
716 sbuf_chr(sb, *ln++);
717 return sbuf_done(sb);
720 static void vi_change(int r1, int c1, int r2, int c2, int lnmode, int closed)
722 char *region;
723 int l1, l2;
724 int row, col;
725 char *rep;
726 char *pref, *post;
727 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
728 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
729 reg_put(vi_ybuf, region, lnmode);
730 free(region);
731 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, l1);
732 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
733 rep = vi_input(pref, post, &row, &col);
734 if (rep) {
735 lbuf_rm(xb, r1, r2 + 1);
736 lbuf_put(xb, r1, rep);
737 xrow = r1 + row - 1;
738 xcol = col;
739 free(rep);
741 free(pref);
742 free(post);
745 static void vi_pipe(int r1, int r2)
747 char *text;
748 char *rep;
749 char *cmd = vi_prompt("!");
750 if (!cmd)
751 return;
752 if (r2 < r1)
753 swap(&r1, &r2);
754 text = lbuf_cp(xb, r1, r2 + 1);
755 rep = cmd_pipe(cmd, text);
756 if (rep) {
757 lbuf_rm(xb, r1, r2 + 1);
758 lbuf_put(xb, r1, rep);
760 free(cmd);
761 free(text);
762 free(rep);
765 static void vi_shift(int r1, int r2, int dir)
767 struct sbuf *sb;
768 char *ln;
769 int i;
770 if (r2 < r1)
771 swap(&r1, &r2);
772 for (i = r1; i <= r2; i++) {
773 if (!(ln = lbuf_get(xb, i)))
774 continue;
775 sb = sbuf_make();
776 if (dir > 0)
777 sbuf_chr(sb, '\t');
778 else
779 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
780 sbuf_str(sb, ln);
781 lbuf_rm(xb, i, i + 1);
782 lbuf_put(xb, i, sbuf_buf(sb));
783 sbuf_free(sb);
785 xrow = r1;
786 lbuf_postindents(xb, &xrow, &xcol);
789 static int vc_motion(int cmd)
791 int r1 = xrow, r2 = xrow; /* region rows */
792 int c1 = xcol, c2 = xcol; /* visual region columns */
793 int lnmode = 0; /* line-based region */
794 int closed = 1; /* include the last character */
795 int mv;
796 vi_arg2 = vi_prefix();
797 if (vi_arg2 < 0)
798 return 1;
799 if ((mv = vi_motionln(&r2, cmd))) {
800 c2 = -1;
801 } else if (!(mv = vi_motion(&r2, &c2))) {
802 vi_read();
803 return 1;
805 if (mv < 0)
806 return 1;
807 if (!strchr("fFtTeE$", mv))
808 closed = 0;
809 lnmode = c2 < 0;
810 if (lnmode) {
811 lbuf_eol(xb, &r1, &c1, -1);
812 lbuf_eol(xb, &r2, &c2, +1);
814 if (cmd == 'y')
815 vi_yank(r1, c1, r2, c2, lnmode, closed);
816 if (cmd == 'd')
817 vi_delete(r1, c1, r2, c2, lnmode, closed);
818 if (cmd == 'c')
819 vi_change(r1, c1, r2, c2, lnmode, closed);
820 if (cmd == '!')
821 vi_pipe(r1, r2);
822 if (cmd == '>' || cmd == '<')
823 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
824 return 0;
827 static int vc_insert(int cmd)
829 char *pref, *post;
830 char *ln = lbuf_get(xb, xrow);
831 int row, col, off = 0;
832 char *rep;
833 if (cmd == 'I')
834 lbuf_postindents(xb, &xrow, &xcol);
835 if (cmd == 'A') {
836 lbuf_eol(xb, &xrow, &xcol, +1);
837 lbuf_lnnext(xb, &xrow, &xcol, -1);
839 if (cmd == 'o')
840 xrow += 1;
841 if (cmd == 'i' || cmd == 'I')
842 off = vi_insertionoffset(ln, xcol, 1);
843 if (cmd == 'a' || cmd == 'A')
844 off = vi_insertionoffset(ln, xcol, 0);
845 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
846 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
847 rep = vi_input(pref, post, &row, &col);
848 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
849 lbuf_put(xb, 0, "\n");
850 if (rep) {
851 if (cmd != 'o' && cmd != 'O')
852 lbuf_rm(xb, xrow, xrow + 1);
853 lbuf_put(xb, xrow, rep);
854 xrow += row - 1;
855 xcol = col;
856 free(rep);
858 free(pref);
859 free(post);
860 return !rep;
863 static int vc_put(int cmd)
865 int cnt = MAX(1, vi_arg1);
866 int lnmode;
867 char *ln;
868 char *buf = reg_get(vi_ybuf, &lnmode);
869 struct sbuf *sb;
870 int off;
871 int i;
872 if (!buf)
873 return 1;
874 ln = lnmode ? NULL : lbuf_get(xb, xrow);
875 off = vi_insertionoffset(ln, xcol, cmd == 'P');
876 if (cmd == 'p' && !ln)
877 xrow++;
878 sb = sbuf_make();
879 if (ln) {
880 char *s = uc_sub(ln, 0, off);
881 sbuf_str(sb, s);
882 free(s);
884 for (i = 0; i < cnt; i++)
885 sbuf_str(sb, buf);
886 if (ln) {
887 char *s = uc_sub(ln, off, -1);
888 sbuf_str(sb, s);
889 free(s);
891 if (!ln && !lbuf_len(xb))
892 lbuf_put(xb, 0, "\n");
893 if (ln)
894 lbuf_rm(xb, xrow, xrow + 1);
895 lbuf_put(xb, xrow, sbuf_buf(sb));
896 if (ln)
897 xcol = ren_pos(lbuf_get(xb, xrow), off + uc_slen(buf) * cnt - 1);
898 else
899 lbuf_postindents(xb, &xrow, &xcol);
900 sbuf_free(sb);
901 return 0;
904 static int join_spaces(char *prev, char *next)
906 int prevlen = strlen(prev);
907 if (!prev[0])
908 return 0;
909 if (prev[prevlen - 1] == ' ' || next[0] == ')')
910 return 0;
911 return prev[prevlen - 1] == '.' ? 2 : 1;
914 static int vc_join(void)
916 struct sbuf *sb;
917 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
918 int beg = xrow;
919 int end = xrow + cnt;
920 int off = 0;
921 int i;
922 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
923 return 1;
924 sb = sbuf_make();
925 for (i = beg; i < end; i++) {
926 char *ln = lbuf_get(xb, i);
927 char *lnend = strchr(ln, '\n');
928 int spaces;
929 if (i > beg)
930 while (ln[0] == ' ' || ln[0] == '\t')
931 ln++;
932 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
933 off = uc_slen(sbuf_buf(sb));
934 while (spaces--)
935 sbuf_chr(sb, ' ');
936 sbuf_mem(sb, ln, lnend - ln);
938 sbuf_chr(sb, '\n');
939 lbuf_rm(xb, beg, end);
940 lbuf_put(xb, beg, sbuf_buf(sb));
941 xcol = ren_pos(sbuf_buf(sb), off);
942 sbuf_free(sb);
943 return 0;
946 static int vi_scrollforeward(int cnt)
948 if (xtop >= lbuf_len(xb) - 1)
949 return 1;
950 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
951 xrow = MAX(xrow, xtop);
952 return 0;
955 static int vi_scrollbackward(int cnt)
957 if (xtop == 0)
958 return 1;
959 xtop = MAX(0, xtop - cnt);
960 xrow = MIN(xrow, xtop + xrows - 1);
961 return 0;
964 static void vc_status(void)
966 snprintf(xmsg, sizeof(xmsg), "\"%s\" line %d of %d, col %d\n",
967 xpath[0] ? xpath : "unnamed", xrow + 1, lbuf_len(xb), xcol + 1);
970 static int vc_replace(void)
972 int cnt = MAX(1, vi_arg1);
973 char *cs = vi_char();
974 char *ln = lbuf_get(xb, xrow);
975 struct sbuf *sb;
976 char *pref, *post;
977 char *s;
978 int off, i;
979 if (!ln || !cs)
980 return 1;
981 off = ren_off(ln, xcol);
982 s = uc_chr(ln, off);
983 for (i = 0; s[0] != '\n' && i < cnt; i++)
984 s = uc_next(s);
985 if (i < cnt)
986 return 1;
987 pref = uc_sub(ln, 0, off);
988 post = uc_sub(ln, off + cnt, -1);
989 sb = sbuf_make();
990 sbuf_str(sb, pref);
991 for (i = 0; i < cnt; i++)
992 sbuf_str(sb, cs);
993 sbuf_str(sb, post);
994 lbuf_rm(xb, xrow, xrow + 1);
995 lbuf_put(xb, xrow, sbuf_buf(sb));
996 off += cnt - 1;
997 xcol = ren_pos(sbuf_buf(sb), off);
998 sbuf_free(sb);
999 free(pref);
1000 free(post);
1001 return 0;
1004 static void vi(void)
1006 int mark;
1007 char *ln;
1008 term_init();
1009 xtop = 0;
1010 xrow = 0;
1011 lbuf_eol(xb, &xrow, &xcol, -1);
1012 vi_draw();
1013 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
1014 while (!xquit) {
1015 int redraw = 0;
1016 int orow = xrow;
1017 int ocol = xcol;
1018 int mv, n;
1019 vi_arg2 = 0;
1020 vi_ybuf = vi_yankbuf();
1021 vi_arg1 = vi_prefix();
1022 if (!vi_ybuf)
1023 vi_ybuf = vi_yankbuf();
1024 mv = vi_motion(&xrow, &xcol);
1025 if (mv > 0) {
1026 if (strchr("\'GHML/?", mv))
1027 lbuf_mark(xb, '\'', orow);
1028 if (xcol < 0) {
1029 if (strchr("jk", mv))
1030 xcol = ocol;
1031 else
1032 lbuf_postindents(xb, &xrow, &xcol);
1034 } else if (mv == 0) {
1035 int c = vi_read();
1036 int z;
1037 if (c <= 0)
1038 continue;
1039 switch (c) {
1040 case TK_CTL('b'):
1041 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1042 break;
1043 lbuf_postindents(xb, &xrow, &xcol);
1044 redraw = 1;
1045 break;
1046 case TK_CTL('f'):
1047 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1048 break;
1049 lbuf_postindents(xb, &xrow, &xcol);
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 'u':
1063 lbuf_undo(xb);
1064 redraw = 1;
1065 break;
1066 case TK_CTL('r'):
1067 lbuf_redo(xb);
1068 redraw = 1;
1069 break;
1070 case TK_CTL('g'):
1071 vc_status();
1072 break;
1073 case TK_CTL('^'):
1074 ex_command("e #");
1075 redraw = 1;
1076 break;
1077 case ':':
1078 ln = vi_prompt(":");
1079 if (ln && ln[0]) {
1080 ex_command(ln);
1081 redraw = 1;
1083 free(ln);
1084 if (xquit)
1085 continue;
1086 break;
1087 case 'c':
1088 case 'd':
1089 case 'y':
1090 case '!':
1091 case '>':
1092 case '<':
1093 if (!vc_motion(c))
1094 redraw = 1;
1095 break;
1096 case 'i':
1097 case 'I':
1098 case 'a':
1099 case 'A':
1100 case 'o':
1101 case 'O':
1102 if (!vc_insert(c))
1103 redraw = 1;
1104 break;
1105 case 'J':
1106 if (!vc_join())
1107 redraw = 1;
1108 break;
1109 case 'm':
1110 if ((mark = vi_read()) > 0 && isalpha(mark))
1111 lbuf_mark(xb, mark, xrow);
1112 break;
1113 case 'p':
1114 case 'P':
1115 if (!vc_put(c))
1116 redraw = 1;
1117 break;
1118 case 'z':
1119 z = vi_read();
1120 switch (z) {
1121 case '\n':
1122 xtop = vi_arg1 ? vi_arg1 : xrow;
1123 break;
1124 case '.':
1125 n = vi_arg1 ? vi_arg1 : xrow;
1126 xtop = MAX(0, n - xrows / 2);
1127 break;
1128 case '-':
1129 n = vi_arg1 ? vi_arg1 : xrow;
1130 xtop = MAX(0, n - xrows + 1);
1131 break;
1132 case 'l':
1133 case 'r':
1134 case 'L':
1135 case 'R':
1136 xdir = z;
1137 break;
1139 redraw = 1;
1140 break;
1141 case 'x':
1142 vi_back(' ');
1143 if (!vc_motion('d'))
1144 redraw = 1;
1145 break;
1146 case 'X':
1147 vi_back(TK_CTL('h'));
1148 if (!vc_motion('d'))
1149 redraw = 1;
1150 break;
1151 case 'C':
1152 vi_back('$');
1153 if (!vc_motion('c'))
1154 redraw = 1;
1155 break;
1156 case 'D':
1157 vi_back('$');
1158 if (!vc_motion('d'))
1159 redraw = 1;
1160 break;
1161 case 'r':
1162 if (!vc_replace())
1163 redraw = 1;
1164 break;
1165 case 's':
1166 vi_back(' ');
1167 if (!vc_motion('c'))
1168 redraw = 1;
1169 break;
1170 case 'S':
1171 vi_back('c');
1172 if (!vc_motion('c'))
1173 redraw = 1;
1174 break;
1175 case 'Y':
1176 vi_back('y');
1177 if (!vc_motion('y'))
1178 redraw = 1;
1179 break;
1180 default:
1181 continue;
1184 if (xrow < 0 || xrow >= lbuf_len(xb))
1185 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1186 if (xrow < xtop || xrow >= xtop + xrows) {
1187 xtop = xrow < xtop ? xrow : MAX(0, xrow - xrows + 1);
1188 redraw = 1;
1190 if (redraw)
1191 vi_draw();
1192 if (xmsg[0])
1193 vi_drawmsg();
1194 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1195 ren_cursor(lbuf_get(xb, xrow), xcol)));
1196 lbuf_undomark(xb);
1198 term_pos(xrows, 0);
1199 term_kill();
1200 term_done();
1203 int main(int argc, char *argv[])
1205 char ecmd[PATHLEN];
1206 int i;
1207 xb = lbuf_make();
1208 xvis = 1;
1209 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1210 if (argv[i][1] == 's')
1211 xled = 0;
1212 if (argv[i][1] == 'e')
1213 xvis = 0;
1214 if (argv[i][1] == 'v')
1215 xvis = 1;
1217 dir_init();
1218 if (i < argc) {
1219 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1220 ex_command(ecmd);
1222 if (xvis)
1223 vi();
1224 else
1225 ex();
1226 lbuf_free(xb);
1227 reg_done();
1228 dir_done();
1229 return 0;