ex: td option for text direction
[neatvi.git] / vi.c
blob968bc803782833c4e5e524616930884a7d72df8d
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 vi_msg[512]; /* current message */
16 static char vi_findlast[256]; /* the last searched keyword */
17 static int vi_finddir; /* the last search direction */
18 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
19 static int vi_charcmd; /* the character finding command */
20 static int vi_arg1, vi_arg2; /* the first and second arguments */
21 static int vi_ybuf; /* current yank buffer */
23 static void vi_drawmsg(void)
25 led_print(vi_msg, xrows);
26 vi_msg[0] = '\0';
29 static void vi_draw(void)
31 int i;
32 term_record();
33 for (i = xtop; i < xtop + xrows; i++) {
34 char *s = lbuf_get(xb, i);
35 led_print(s ? s : (i ? "~" : ""), i - xtop);
37 vi_drawmsg();
38 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
39 term_commit();
42 static int vi_buf[128];
43 static int vi_buflen;
45 static int vi_read(void)
47 return vi_buflen ? vi_buf[--vi_buflen] : term_read(1000);
50 static void vi_back(int c)
52 if (vi_buflen < sizeof(vi_buf))
53 vi_buf[vi_buflen++] = c;
56 static char *vi_char(void)
58 int key = vi_read();
59 return TK_INT(key) ? NULL : led_keymap(key);
62 static char *vi_prompt(char *msg)
64 term_pos(xrows, led_pos(msg, 0));
65 term_kill();
66 return led_prompt(msg, "");
69 char *ex_read(char *msg)
71 struct sbuf *sb;
72 char c;
73 if (xled) {
74 char *s = led_prompt(msg, "");
75 if (s)
76 term_chr('\n');
77 return s;
79 sb = sbuf_make();
80 while ((c = getchar()) != EOF && c != '\n')
81 sbuf_chr(sb, c);
82 if (c == EOF) {
83 sbuf_free(sb);
84 return NULL;
86 return sbuf_done(sb);
89 void ex_show(char *msg)
91 if (xvis) {
92 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
93 } else if (xled) {
94 led_print(msg, -1);
95 term_chr('\n');
96 } else {
97 printf("%s", msg);
101 static int vi_yankbuf(void)
103 int c = vi_read();
104 if (c == '"')
105 return vi_read();
106 vi_back(c);
107 return 0;
110 static int vi_prefix(void)
112 int n = 0;
113 int c = vi_read();
114 if ((c >= '1' && c <= '9')) {
115 while (isdigit(c)) {
116 n = n * 10 + c - '0';
117 c = vi_read();
120 vi_back(c);
121 return n;
124 static int lbuf_lnnext(struct lbuf *lb, int *r, int *c, int dir)
126 char *ln = lbuf_get(lb, *r);
127 int col = ln ? ren_next(ln, *c, dir) : -1;
128 if (col < 0)
129 return -1;
130 *c = col;
131 return 0;
134 static void lbuf_eol(struct lbuf *lb, int *r, int *c, int dir)
136 char *ln = lbuf_get(lb, *r);
137 *c = dir < 0 ? 0 : MAX(0, ren_wid(ln ? ln : "") - 1);
140 static int lbuf_next(struct lbuf *lb, int *r, int *c, int dir)
142 if (dir < 0 && *r >= lbuf_len(lb))
143 *r = MAX(0, lbuf_len(lb) - 1);
144 if (lbuf_lnnext(lb, r, c, dir)) {
145 if (!lbuf_get(lb, *r + dir))
146 return -1;
147 *r += dir;
148 lbuf_eol(lb, r, c, -dir);
149 return 0;
151 return 0;
154 /* return a pointer to the character at visual position c of line r */
155 static char *lbuf_chr(struct lbuf *lb, int r, int c)
157 char *ln = lbuf_get(lb, r);
158 return ln ? uc_chr(ln, ren_off(ln, c)) : "";
161 static void lbuf_postindents(struct lbuf *lb, int *r, int *c)
163 lbuf_eol(lb, r, c, -1);
164 while (uc_isspace(lbuf_chr(lb, *r, *c)))
165 if (lbuf_lnnext(lb, r, c, +1))
166 break;
169 static int lbuf_findchar(struct lbuf *lb, int *row, int *col, char *cs, int cmd, int n)
171 int dir = (cmd == 'f' || cmd == 't') ? +1 : -1;
172 int c = *col;
173 if (n < 0)
174 dir = -dir;
175 if (n < 0)
176 n = -n;
177 strcpy(vi_charlast, cs);
178 vi_charcmd = cmd;
179 while (n > 0 && !lbuf_lnnext(lb, row, &c, dir))
180 if (uc_code(lbuf_chr(lb, *row, c)) == uc_code(cs))
181 n--;
182 if (!n)
183 *col = c;
184 if (!n && (cmd == 't' || cmd == 'T'))
185 lbuf_lnnext(lb, row, col, -dir);
186 return n != 0;
189 static int lbuf_search(struct lbuf *lb, char *kw, int dir, int *r, int *c, int *len)
191 int offs[2];
192 int found = 0;
193 int row = *r, col = *c;
194 int i;
195 struct rset *re = rset_make(1, &kw, xic ? RE_ICASE : 0);
196 if (!re)
197 return 1;
198 for (i = row; !found && i >= 0 && i < lbuf_len(lb); i += dir) {
199 char *s = lbuf_get(lb, i);
200 int off = dir > 0 && row == i ? uc_chr(s, col + 1) - s : 0;
201 int flg = off ? RE_NOTBOL : 0;
202 while (rset_find(re, s + off, 1, offs, flg) >= 0) {
203 if (dir < 0 && row == i && off + offs[0] >= col)
204 break;
205 found = 1;
206 *c = uc_off(s, off + offs[0]);
207 *r = i;
208 *len = offs[1] - offs[0];
209 off += offs[1];
210 if (dir > 0)
211 break;
214 rset_free(re);
215 return !found;
218 static int vi_search(int cmd, int cnt, int *row, int *col)
220 int r = *row;
221 int c = *col;
222 int failed = 0;
223 int len = 0;
224 int i, dir;
225 char *off = "";
226 if (cmd == '/' || cmd == '?') {
227 char sign[4] = {cmd};
228 char *kw = vi_prompt(sign);
229 if (!kw)
230 return 1;
231 vi_finddir = cmd == '/' ? +1 : -1;
232 if (kw[0])
233 snprintf(vi_findlast, sizeof(vi_findlast), "%s", kw);
234 if (strchr(vi_findlast, cmd)) {
235 off = strchr(vi_findlast, cmd) + 1;
236 *strchr(vi_findlast, cmd) = '\0';
238 free(kw);
240 dir = cmd == 'N' ? -vi_finddir : vi_finddir;
241 if (!vi_findlast[0] || !lbuf_len(xb))
242 return 1;
243 c = ren_off(lbuf_get(xb, *row), *col);
244 for (i = 0; i < cnt; i++) {
245 if (lbuf_search(xb, vi_findlast, dir, &r, &c, &len)) {
246 failed = 1;
247 break;
249 if (i + 1 < cnt && cmd == '/')
250 c += len;
252 if (!failed) {
253 *row = r;
254 *col = ren_pos(lbuf_get(xb, r), c);
255 while (off[0] && isspace((unsigned char) off[0]))
256 off++;
257 if (off[0]) {
258 *col = -1;
259 if (*row + atoi(off) < 0 || *row + atoi(off) >= lbuf_len(xb))
260 failed = 1;
261 else
262 *row += atoi(off);
265 if (failed)
266 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", vi_findlast);
267 return failed;
270 /* move to the last character of the word */
271 static int lbuf_wordlast(struct lbuf *lb, int *row, int *col, int kind, int dir)
273 if (!kind || !(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
274 return 0;
275 while (uc_kind(lbuf_chr(lb, *row, *col)) & kind)
276 if (lbuf_next(lb, row, col, dir))
277 return 1;
278 if (!(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
279 lbuf_next(lb, row, col, -dir);
280 return 0;
283 static int lbuf_wordbeg(struct lbuf *lb, int *row, int *col, int big, int dir)
285 int nl = 0;
286 lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir);
287 if (lbuf_next(lb, row, col, dir))
288 return 1;
289 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
290 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
291 if (nl == 2)
292 return 0;
293 if (lbuf_next(lb, row, col, dir))
294 return 1;
296 return 0;
299 static int lbuf_wordend(struct lbuf *lb, int *row, int *col, int big, int dir)
301 int nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? -1 : 0;
302 if (!uc_isspace(lbuf_chr(lb, *row, *col)))
303 if (lbuf_next(lb, row, col, dir))
304 return 1;
305 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
306 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
307 if (nl == 2) {
308 if (dir < 0)
309 lbuf_next(lb, row, col, -dir);
310 return 0;
312 if (lbuf_next(lb, row, col, dir))
313 return 1;
315 if (lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir))
316 return 1;
317 return 0;
320 static int lbuf_paragraphbeg(struct lbuf *lb, int *row, int *col, int dir)
322 while (*row >= 0 && *row < lbuf_len(lb) && !strcmp("\n", lbuf_get(lb, *row)))
323 *row += dir;
324 while (*row >= 0 && *row < lbuf_len(lb) && strcmp("\n", lbuf_get(lb, *row)))
325 *row += dir;
326 *row = MAX(0, MIN(*row, lbuf_len(lb) - 1));
327 lbuf_eol(lb, row, col, -1);
328 return 0;
331 static int lbuf_sectionbeg(struct lbuf *lb, int *row, int *col, int dir)
333 *row += dir;
334 while (*row >= 0 && *row < lbuf_len(lb) && lbuf_get(lb, *row)[0] != '{')
335 *row += dir;
336 *row = MAX(0, MIN(*row, lbuf_len(lb) - 1));
337 lbuf_eol(lb, row, col, -1);
338 return 0;
341 /* read a line motion */
342 static int vi_motionln(int *row, int cmd)
344 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
345 int c = vi_read();
346 int mark;
347 switch (c) {
348 case '\n':
349 case '+':
350 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
351 break;
352 case '-':
353 *row = MAX(*row - cnt, 0);
354 break;
355 case '_':
356 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
357 break;
358 case '\'':
359 if ((mark = vi_read()) > 0 && (isalpha(mark) || mark == '\''))
360 if (lbuf_markpos(xb, mark) >= 0)
361 *row = lbuf_markpos(xb, mark);
362 break;
363 case 'j':
364 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
365 break;
366 case 'k':
367 *row = MAX(*row - cnt, 0);
368 break;
369 case 'G':
370 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
371 break;
372 case 'H':
373 if (lbuf_len(xb))
374 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
375 else
376 *row = 0;
377 break;
378 case 'L':
379 if (lbuf_len(xb))
380 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
381 else
382 *row = 0;
383 break;
384 case 'M':
385 if (lbuf_len(xb))
386 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
387 else
388 *row = 0;
389 break;
390 default:
391 if (c == cmd) {
392 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
393 break;
395 vi_back(c);
396 return 0;
398 return c;
401 /* read a motion */
402 static int vi_motion(int *row, int *col)
404 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
405 char *ln = lbuf_get(xb, *row);
406 int dir = dir_context(ln ? ln : "");
407 char *cs;
408 int mv;
409 int i;
410 if ((mv = vi_motionln(row, 0))) {
411 *col = -1;
412 return mv;
414 mv = vi_read();
415 switch (mv) {
416 case ' ':
417 for (i = 0; i < cnt; i++)
418 if (lbuf_lnnext(xb, row, col, 1))
419 break;
420 break;
421 case 'f':
422 if (!(cs = vi_char()))
423 return -1;
424 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
425 return -1;
426 break;
427 case 'F':
428 if (!(cs = vi_char()))
429 return -1;
430 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
431 return -1;
432 break;
433 case ';':
434 if (!vi_charlast[0])
435 return -1;
436 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, cnt))
437 return -1;
438 break;
439 case ',':
440 if (!vi_charlast[0])
441 return -1;
442 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, -cnt))
443 return -1;
444 break;
445 case 'h':
446 for (i = 0; i < cnt; i++)
447 if (lbuf_lnnext(xb, row, col, -1 * dir))
448 break;
449 break;
450 case 'l':
451 for (i = 0; i < cnt; i++)
452 if (lbuf_lnnext(xb, row, col, +1 * dir))
453 break;
454 break;
455 case 't':
456 if (!(cs = vi_char()))
457 return -1;
458 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
459 return -1;
460 break;
461 case 'T':
462 if (!(cs = vi_char()))
463 return -1;
464 if (lbuf_findchar(xb, row, col, cs, mv, cnt))
465 return -1;
466 break;
467 case 'B':
468 for (i = 0; i < cnt; i++)
469 if (lbuf_wordend(xb, row, col, 1, -1))
470 break;
471 break;
472 case 'E':
473 for (i = 0; i < cnt; i++)
474 if (lbuf_wordend(xb, row, col, 1, +1))
475 break;
476 break;
477 case 'W':
478 for (i = 0; i < cnt; i++)
479 if (lbuf_wordbeg(xb, row, col, 1, +1))
480 break;
481 break;
482 case 'b':
483 for (i = 0; i < cnt; i++)
484 if (lbuf_wordend(xb, row, col, 0, -1))
485 break;
486 break;
487 case 'e':
488 for (i = 0; i < cnt; i++)
489 if (lbuf_wordend(xb, row, col, 0, +1))
490 break;
491 break;
492 case 'w':
493 for (i = 0; i < cnt; i++)
494 if (lbuf_wordbeg(xb, row, col, 0, +1))
495 break;
496 break;
497 case '{':
498 for (i = 0; i < cnt; i++)
499 if (lbuf_paragraphbeg(xb, row, col, -1))
500 break;
501 break;
502 case '}':
503 for (i = 0; i < cnt; i++)
504 if (lbuf_paragraphbeg(xb, row, col, +1))
505 break;
506 break;
507 case '[':
508 if (vi_read() != '[')
509 return -1;
510 for (i = 0; i < cnt; i++)
511 if (lbuf_sectionbeg(xb, row, col, -1))
512 break;
513 break;
514 case ']':
515 if (vi_read() != ']')
516 return -1;
517 for (i = 0; i < cnt; i++)
518 if (lbuf_sectionbeg(xb, row, col, +1))
519 break;
520 break;
521 case '0':
522 lbuf_eol(xb, row, col, -1);
523 break;
524 case '^':
525 lbuf_postindents(xb, row, col);
526 break;
527 case '$':
528 *col = 1024;
529 break;
530 case '|':
531 *col = cnt - 1;
532 break;
533 case '/':
534 if (vi_search(mv, cnt, row, col))
535 return -1;
536 break;
537 case '?':
538 if (vi_search(mv, cnt, row, col))
539 return -1;
540 break;
541 case 'n':
542 if (vi_search(mv, cnt, row, col))
543 return -1;
544 break;
545 case 'N':
546 if (vi_search(mv, cnt, row, col))
547 return -1;
548 break;
549 case 127:
550 case TK_CTL('h'):
551 for (i = 0; i < cnt; i++)
552 if (lbuf_lnnext(xb, row, col, -1))
553 break;
554 break;
555 default:
556 vi_back(mv);
557 return 0;
559 return mv;
562 static void swap(int *a, int *b)
564 int t = *a;
565 *a = *b;
566 *b = t;
569 static char *lbuf_region(struct lbuf *lb, int r1, int l1, int r2, int l2)
571 struct sbuf *sb;
572 char *s1, *s2, *s3;
573 if (r1 == r2)
574 return uc_sub(lbuf_get(lb, r1), l1, l2);
575 sb = sbuf_make();
576 s1 = uc_sub(lbuf_get(lb, r1), l1, -1);
577 s3 = uc_sub(lbuf_get(lb, r2), 0, l2);
578 s2 = lbuf_cp(lb, r1 + 1, r2);
579 sbuf_str(sb, s1);
580 sbuf_str(sb, s2);
581 sbuf_str(sb, s3);
582 free(s1);
583 free(s2);
584 free(s3);
585 return sbuf_done(sb);
588 /* insertion offset before or after the given visual position */
589 static int vi_insertionoffset(char *s, int c1, int before)
591 int l;
592 if (!s || !*s)
593 return 0;
594 l = ren_off(s, c1);
595 return before || s[l] == '\n' ? l : l + 1;
598 static void vi_commandregion(int *r1, int *r2, int *c1, int *c2, int *l1, int *l2, int closed)
600 if (*r2 < *r1 || (*r2 == *r1 && *c2 < *c1)) {
601 swap(r1, r2);
602 swap(c1, c2);
604 *l1 = vi_insertionoffset(lbuf_get(xb, *r1), *c1, 1);
605 *l2 = vi_insertionoffset(lbuf_get(xb, *r2), *c2, !closed);
606 if (*r1 == *r2 && lbuf_get(xb, *r1))
607 ren_region(lbuf_get(xb, *r1), *c1, *c2, l1, l2, closed);
608 if (*r1 == *r2 && *l2 < *l1)
609 swap(l1, l2);
612 static void vi_yank(int r1, int c1, int r2, int c2, int lnmode, int closed)
614 char *region;
615 int l1, l2;
616 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
617 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
618 reg_put(vi_ybuf, region, lnmode);
619 free(region);
620 xrow = r1;
621 xcol = lnmode ? xcol : c1;
624 static void vi_delete(int r1, int c1, int r2, int c2, int lnmode, int closed)
626 char *pref, *post;
627 char *region;
628 int l1, l2;
629 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
630 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
631 reg_put(vi_ybuf, region, lnmode);
632 free(region);
633 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, l1);
634 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
635 lbuf_rm(xb, r1, r2 + 1);
636 if (!lnmode) {
637 struct sbuf *sb = sbuf_make();
638 sbuf_str(sb, pref);
639 sbuf_str(sb, post);
640 lbuf_put(xb, r1, sbuf_buf(sb));
641 sbuf_free(sb);
643 xrow = r1;
644 xcol = c1;
645 if (lnmode)
646 lbuf_postindents(xb, &xrow, &xcol);
647 free(pref);
648 free(post);
651 static int lastline(char *str)
653 char *s = str;
654 char *r = s;
655 while (s && s[0]) {
656 r = s;
657 s = strchr(s + 1, '\n');
659 return r - str;
662 static int linecount(char *s)
664 int n;
665 for (n = 0; s; n++)
666 if ((s = strchr(s, '\n')))
667 s++;
668 return n;
671 static int indentscopy(char *d, char *s, int len)
673 int i;
674 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
675 d[i] = s[i];
676 d[i] = '\0';
677 return i;
680 static char *vi_input(char *pref, char *post, int *row, int *col)
682 char ai[64] = "";
683 char *rep;
684 struct sbuf *sb;
685 int last, off;
686 if (xai)
687 pref += indentscopy(ai, pref, sizeof(ai));
688 rep = led_input(pref, post, ai, xai ? sizeof(ai) - 1 : 0);
689 if (!rep)
690 return NULL;
691 sb = sbuf_make();
692 sbuf_str(sb, ai);
693 sbuf_str(sb, pref);
694 sbuf_str(sb, rep);
695 last = lastline(sbuf_buf(sb));
696 off = uc_slen(sbuf_buf(sb) + last);
697 if (last)
698 while (xai && (post[0] == ' ' || post[0] == '\t'))
699 post++;
700 sbuf_str(sb, post);
701 *row = linecount(sbuf_buf(sb)) - 1;
702 *col = ren_pos(sbuf_buf(sb) + last, MAX(0, off - 1));
703 free(rep);
704 return sbuf_done(sb);
707 static char *vi_indents(char *ln)
709 struct sbuf *sb = sbuf_make();
710 while (xai && ln && (*ln == ' ' || *ln == '\t'))
711 sbuf_chr(sb, *ln++);
712 return sbuf_done(sb);
715 static void vi_change(int r1, int c1, int r2, int c2, int lnmode, int closed)
717 char *region;
718 int l1, l2;
719 int row, col;
720 char *rep;
721 char *pref, *post;
722 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
723 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
724 reg_put(vi_ybuf, region, lnmode);
725 free(region);
726 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, l1);
727 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
728 rep = vi_input(pref, post, &row, &col);
729 if (rep) {
730 lbuf_rm(xb, r1, r2 + 1);
731 lbuf_put(xb, r1, rep);
732 xrow = r1 + row - 1;
733 xcol = col;
734 free(rep);
736 free(pref);
737 free(post);
740 static void vi_pipe(int r1, int r2)
742 char *text;
743 char *rep;
744 char *cmd = vi_prompt("!");
745 if (!cmd)
746 return;
747 if (r2 < r1)
748 swap(&r1, &r2);
749 text = lbuf_cp(xb, r1, r2 + 1);
750 rep = cmd_pipe(cmd, text);
751 if (rep) {
752 lbuf_rm(xb, r1, r2 + 1);
753 lbuf_put(xb, r1, rep);
755 free(cmd);
756 free(text);
757 free(rep);
760 static void vi_shift(int r1, int r2, int dir)
762 struct sbuf *sb;
763 char *ln;
764 int i;
765 if (r2 < r1)
766 swap(&r1, &r2);
767 for (i = r1; i <= r2; i++) {
768 if (!(ln = lbuf_get(xb, i)))
769 continue;
770 sb = sbuf_make();
771 if (dir > 0)
772 sbuf_chr(sb, '\t');
773 else
774 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
775 sbuf_str(sb, ln);
776 lbuf_rm(xb, i, i + 1);
777 lbuf_put(xb, i, sbuf_buf(sb));
778 sbuf_free(sb);
780 xrow = r1;
781 lbuf_postindents(xb, &xrow, &xcol);
784 static int vc_motion(int cmd)
786 int r1 = xrow, r2 = xrow; /* region rows */
787 int c1 = xcol, c2 = xcol; /* visual region columns */
788 int lnmode = 0; /* line-based region */
789 int closed = 1; /* include the last character */
790 int mv;
791 vi_arg2 = vi_prefix();
792 if (vi_arg2 < 0)
793 return 1;
794 c1 = ren_noeol(lbuf_get(xb, r1), xcol);
795 c2 = c1;
796 if ((mv = vi_motionln(&r2, cmd))) {
797 c2 = -1;
798 } else if (!(mv = vi_motion(&r2, &c2))) {
799 vi_read();
800 return 1;
802 if (mv < 0)
803 return 1;
804 if (!strchr("fFtTeE", mv))
805 closed = 0;
806 lnmode = c2 < 0;
807 if (lnmode) {
808 lbuf_eol(xb, &r1, &c1, -1);
809 lbuf_eol(xb, &r2, &c2, +1);
811 if (cmd == 'y')
812 vi_yank(r1, c1, r2, c2, lnmode, closed);
813 if (cmd == 'd')
814 vi_delete(r1, c1, r2, c2, lnmode, closed);
815 if (cmd == 'c')
816 vi_change(r1, c1, r2, c2, lnmode, closed);
817 if (cmd == '!')
818 vi_pipe(r1, r2);
819 if (cmd == '>' || cmd == '<')
820 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
821 return 0;
824 static int vc_insert(int cmd)
826 char *pref, *post;
827 char *ln = lbuf_get(xb, xrow);
828 int row, col, off = 0;
829 char *rep;
830 if (cmd == 'I')
831 lbuf_postindents(xb, &xrow, &xcol);
832 if (cmd == 'A') {
833 lbuf_eol(xb, &xrow, &xcol, +1);
834 lbuf_lnnext(xb, &xrow, &xcol, -1);
836 xcol = ren_noeol(ln, xcol);
837 if (cmd == 'o')
838 xrow += 1;
839 if (cmd == 'i' || cmd == 'I')
840 off = vi_insertionoffset(ln, xcol, 1);
841 if (cmd == 'a' || cmd == 'A')
842 off = vi_insertionoffset(ln, xcol, 0);
843 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
844 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
845 rep = vi_input(pref, post, &row, &col);
846 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
847 lbuf_put(xb, 0, "\n");
848 if (rep) {
849 if (cmd != 'o' && cmd != 'O')
850 lbuf_rm(xb, xrow, xrow + 1);
851 lbuf_put(xb, xrow, rep);
852 xrow += row - 1;
853 xcol = col;
854 free(rep);
856 free(pref);
857 free(post);
858 return !rep;
861 static int vc_put(int cmd)
863 int cnt = MAX(1, vi_arg1);
864 int lnmode;
865 char *ln;
866 char *buf = reg_get(vi_ybuf, &lnmode);
867 struct sbuf *sb;
868 int off;
869 int i;
870 if (!buf)
871 return 1;
872 ln = lnmode ? NULL : lbuf_get(xb, xrow);
873 off = vi_insertionoffset(ln, xcol, cmd == 'P');
874 if (cmd == 'p' && !ln)
875 xrow++;
876 sb = sbuf_make();
877 if (ln) {
878 char *s = uc_sub(ln, 0, off);
879 sbuf_str(sb, s);
880 free(s);
882 for (i = 0; i < cnt; i++)
883 sbuf_str(sb, buf);
884 if (ln) {
885 char *s = uc_sub(ln, off, -1);
886 sbuf_str(sb, s);
887 free(s);
889 if (!ln && !lbuf_len(xb))
890 lbuf_put(xb, 0, "\n");
891 if (ln)
892 lbuf_rm(xb, xrow, xrow + 1);
893 lbuf_put(xb, xrow, sbuf_buf(sb));
894 if (ln)
895 xcol = ren_pos(lbuf_get(xb, xrow), off + uc_slen(buf) * cnt - 1);
896 else
897 lbuf_postindents(xb, &xrow, &xcol);
898 sbuf_free(sb);
899 return 0;
902 static int join_spaces(char *prev, char *next)
904 int prevlen = strlen(prev);
905 if (!prev[0])
906 return 0;
907 if (prev[prevlen - 1] == ' ' || next[0] == ')')
908 return 0;
909 return prev[prevlen - 1] == '.' ? 2 : 1;
912 static int vc_join(void)
914 struct sbuf *sb;
915 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
916 int beg = xrow;
917 int end = xrow + cnt;
918 int off = 0;
919 int i;
920 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
921 return 1;
922 sb = sbuf_make();
923 for (i = beg; i < end; i++) {
924 char *ln = lbuf_get(xb, i);
925 char *lnend = strchr(ln, '\n');
926 int spaces;
927 if (i > beg)
928 while (ln[0] == ' ' || ln[0] == '\t')
929 ln++;
930 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
931 off = uc_slen(sbuf_buf(sb));
932 while (spaces--)
933 sbuf_chr(sb, ' ');
934 sbuf_mem(sb, ln, lnend - ln);
936 sbuf_chr(sb, '\n');
937 lbuf_rm(xb, beg, end);
938 lbuf_put(xb, beg, sbuf_buf(sb));
939 xcol = ren_pos(sbuf_buf(sb), off);
940 sbuf_free(sb);
941 return 0;
944 static int vi_scrollforeward(int cnt)
946 if (xtop >= lbuf_len(xb) - 1)
947 return 1;
948 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
949 xrow = MAX(xrow, xtop);
950 return 0;
953 static int vi_scrollbackward(int cnt)
955 if (xtop == 0)
956 return 1;
957 xtop = MAX(0, xtop - cnt);
958 xrow = MIN(xrow, xtop + xrows - 1);
959 return 0;
962 static void vc_status(void)
964 int pos = ren_noeol(lbuf_get(xb, xrow), xcol);
965 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" line %d of %d, col %d\n",
966 xpath[0] ? xpath : "unnamed", xrow + 1, lbuf_len(xb),
967 ren_cursor(lbuf_get(xb, xrow), pos) + 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 nrow = xrow;
1017 int ncol = ren_noeol(lbuf_get(xb, xrow), xcol);
1018 int otop = xtop;
1019 int mv, n;
1020 vi_arg2 = 0;
1021 vi_ybuf = vi_yankbuf();
1022 vi_arg1 = vi_prefix();
1023 if (!vi_ybuf)
1024 vi_ybuf = vi_yankbuf();
1025 mv = vi_motion(&nrow, &ncol);
1026 if (mv > 0) {
1027 if (strchr("\'GHML/?{}[]", mv))
1028 lbuf_mark(xb, '\'', xrow);
1029 xrow = nrow;
1030 if (ncol < 0) {
1031 if (!strchr("jk", mv))
1032 lbuf_postindents(xb, &xrow, &xcol);
1033 } else {
1034 if (strchr("|$", mv))
1035 xcol = ncol;
1036 else
1037 xcol = ren_noeol(lbuf_get(xb, xrow), ncol);
1039 } else if (mv == 0) {
1040 int c = vi_read();
1041 int z;
1042 if (c <= 0)
1043 continue;
1044 switch (c) {
1045 case TK_CTL('b'):
1046 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1047 break;
1048 lbuf_postindents(xb, &xrow, &xcol);
1049 redraw = 1;
1050 break;
1051 case TK_CTL('f'):
1052 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1053 break;
1054 lbuf_postindents(xb, &xrow, &xcol);
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 'u':
1068 lbuf_undo(xb);
1069 redraw = 1;
1070 break;
1071 case TK_CTL('r'):
1072 lbuf_redo(xb);
1073 redraw = 1;
1074 break;
1075 case TK_CTL('g'):
1076 vc_status();
1077 break;
1078 case TK_CTL('^'):
1079 ex_command("e #");
1080 redraw = 1;
1081 break;
1082 case ':':
1083 ln = vi_prompt(":");
1084 if (ln && ln[0]) {
1085 ex_command(ln);
1086 redraw = 1;
1088 free(ln);
1089 if (xquit)
1090 continue;
1091 break;
1092 case 'c':
1093 case 'd':
1094 case 'y':
1095 case '!':
1096 case '>':
1097 case '<':
1098 if (!vc_motion(c))
1099 redraw = 1;
1100 break;
1101 case 'i':
1102 case 'I':
1103 case 'a':
1104 case 'A':
1105 case 'o':
1106 case 'O':
1107 if (!vc_insert(c))
1108 redraw = 1;
1109 break;
1110 case 'J':
1111 if (!vc_join())
1112 redraw = 1;
1113 break;
1114 case 'm':
1115 if ((mark = vi_read()) > 0 && isalpha(mark))
1116 lbuf_mark(xb, mark, xrow);
1117 break;
1118 case 'p':
1119 case 'P':
1120 if (!vc_put(c))
1121 redraw = 1;
1122 break;
1123 case 'z':
1124 z = vi_read();
1125 switch (z) {
1126 case '\n':
1127 xtop = vi_arg1 ? vi_arg1 : xrow;
1128 break;
1129 case '.':
1130 n = vi_arg1 ? vi_arg1 : xrow;
1131 xtop = MAX(0, n - xrows / 2);
1132 break;
1133 case '-':
1134 n = vi_arg1 ? vi_arg1 : xrow;
1135 xtop = MAX(0, n - xrows + 1);
1136 break;
1137 case 'l':
1138 case 'r':
1139 xdir = z == 'r' ? -1 : +1;
1140 break;
1141 case 'L':
1142 case 'R':
1143 xdir = z == 'R' ? -2 : +2;
1144 break;
1146 redraw = 1;
1147 break;
1148 case 'x':
1149 vi_back(' ');
1150 if (!vc_motion('d'))
1151 redraw = 1;
1152 break;
1153 case 'X':
1154 vi_back(TK_CTL('h'));
1155 if (!vc_motion('d'))
1156 redraw = 1;
1157 break;
1158 case 'C':
1159 vi_back('$');
1160 if (!vc_motion('c'))
1161 redraw = 1;
1162 break;
1163 case 'D':
1164 vi_back('$');
1165 if (!vc_motion('d'))
1166 redraw = 1;
1167 break;
1168 case 'r':
1169 if (!vc_replace())
1170 redraw = 1;
1171 break;
1172 case 's':
1173 vi_back(' ');
1174 if (!vc_motion('c'))
1175 redraw = 1;
1176 break;
1177 case 'S':
1178 vi_back('c');
1179 if (!vc_motion('c'))
1180 redraw = 1;
1181 break;
1182 case 'Y':
1183 vi_back('y');
1184 if (!vc_motion('y'))
1185 redraw = 1;
1186 break;
1187 default:
1188 continue;
1191 if (xrow < 0 || xrow >= lbuf_len(xb))
1192 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1193 if (xtop > xrow)
1194 xtop = xtop - xrows / 2 > xrow ?
1195 MAX(0, xrow - xrows / 2) : xrow;
1196 if (xtop + xrows <= xrow)
1197 xtop = xtop + xrows + xrows / 2 <= xrow ?
1198 xrow - xrows / 2 : xrow - xrows + 1;
1199 if (redraw || xtop != otop)
1200 vi_draw();
1201 if (vi_msg[0])
1202 vi_drawmsg();
1203 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1204 ren_cursor(lbuf_get(xb, xrow), xcol)));
1205 lbuf_undomark(xb);
1207 term_pos(xrows, 0);
1208 term_kill();
1209 term_done();
1212 int main(int argc, char *argv[])
1214 char ecmd[PATHLEN];
1215 int i;
1216 xb = lbuf_make();
1217 xvis = 1;
1218 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1219 if (argv[i][1] == 's')
1220 xled = 0;
1221 if (argv[i][1] == 'e')
1222 xvis = 0;
1223 if (argv[i][1] == 'v')
1224 xvis = 1;
1226 dir_init();
1227 if (i < argc) {
1228 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1229 ex_command(ecmd);
1231 if (xvis)
1232 vi();
1233 else
1234 ex();
1235 lbuf_free(xb);
1236 reg_done();
1237 dir_done();
1238 return 0;