vi: redraw if commands like x change visible columns
[neatvi.git] / vi.c
blobd598a37130a63bea3b8ae7c5d36c4c887ab243ee
1 /*
2 * NEATVI Editor
4 * Copyright (C) 2015-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "vi.h"
25 static char vi_msg[EXLEN]; /* current message */
26 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
27 static int vi_charcmd; /* the character finding command */
28 static int vi_arg1, vi_arg2; /* the first and second arguments */
29 static int vi_ybuf; /* current yank buffer */
30 static int vi_pcol; /* the column requested by | command */
31 static int vi_printed; /* ex_print() calls since the last command */
32 static int vi_scroll; /* scroll amount for ^f and ^d*/
33 static int vi_soset, vi_so; /* search offset; 1 in "/kw/1" */
35 static void vi_wait(void)
37 if (vi_printed > 1) {
38 free(ex_read("[enter to continue]"));
39 vi_msg[0] = '\0';
41 vi_printed = 0;
44 static void vi_drawmsg(void)
46 int oleft = xleft;
47 xleft = 0;
48 led_print(vi_msg, xrows);
49 vi_msg[0] = '\0';
50 xleft = oleft;
53 static void vi_drawrow(int row)
55 char *s = lbuf_get(xb, row);
56 led_print(s ? s : (row ? "~" : ""), row - xtop);
59 /* redraw the screen */
60 static void vi_drawagain(int xcol, int lineonly)
62 int i;
63 term_record();
64 for (i = xtop; i < xtop + xrows; i++)
65 if (!lineonly || i == xrow)
66 vi_drawrow(i);
67 vi_drawmsg();
68 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
69 term_commit();
72 /* update the screen */
73 static void vi_drawupdate(int xcol, int otop)
75 int i;
76 if (otop != xtop) {
77 term_record();
78 term_pos(0, 0);
79 term_room(otop - xtop);
80 if (xtop > otop) {
81 int n = MIN(xtop - otop, xrows);
82 for (i = 0; i < n; i++)
83 vi_drawrow(xtop + xrows - n + i);
84 } else {
85 int n = MIN(otop - xtop, xrows);
86 for (i = 0; i < n; i++)
87 vi_drawrow(xtop + i);
89 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
90 term_commit();
92 vi_drawmsg();
93 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
96 /* update the screen by removing lines r1 to r2 before an input command */
97 static void vi_drawrm(int r1, int r2, int newln)
99 r1 = MIN(MAX(r1, xtop), xtop + xrows);
100 r2 = MIN(MAX(r2, xtop), xtop + xrows);
101 term_pos(r1 - xtop, 0);
102 term_room(r1 - r2 + newln);
105 static int vi_buf[128];
106 static int vi_buflen;
108 static int vi_read(void)
110 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
113 static void vi_back(int c)
115 if (vi_buflen < sizeof(vi_buf))
116 vi_buf[vi_buflen++] = c;
119 static char *vi_char(void)
121 return led_read(ex_kmap());
124 static char *vi_prompt(char *msg, char **kmap)
126 char *r, *s;
127 term_pos(xrows, led_pos(msg, 0));
128 term_kill();
129 s = led_prompt(msg, "", kmap);
130 if (!s)
131 return NULL;
132 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
133 free(s);
134 return r;
137 /* read an ex input line */
138 char *ex_read(char *msg)
140 struct sbuf *sb;
141 char c;
142 if (xled) {
143 char *s = led_prompt(msg, "", ex_kmap());
144 if (s)
145 term_chr('\n');
146 return s;
148 sb = sbuf_make();
149 while ((c = getchar()) != EOF && c != '\n')
150 sbuf_chr(sb, c);
151 if (c == EOF) {
152 sbuf_free(sb);
153 return NULL;
155 return sbuf_done(sb);
158 /* show an ex message */
159 void ex_show(char *msg)
161 if (xvis) {
162 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
163 } else if (xled) {
164 led_print(msg, -1);
165 term_chr('\n');
166 } else {
167 printf("%s", msg);
171 /* print an ex output line */
172 void ex_print(char *line)
174 if (xvis) {
175 vi_printed += line ? 1 : 2;
176 if (line)
177 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
178 if (line)
179 led_print(line, -1);
180 term_chr('\n');
181 } else {
182 if (line)
183 ex_show(line);
187 static int vi_yankbuf(void)
189 int c = vi_read();
190 if (c == '"')
191 return vi_read();
192 vi_back(c);
193 return 0;
196 static int vi_prefix(void)
198 int n = 0;
199 int c = vi_read();
200 if ((c >= '1' && c <= '9')) {
201 while (isdigit(c)) {
202 n = n * 10 + c - '0';
203 c = vi_read();
206 vi_back(c);
207 return n;
210 static int vi_col2off(struct lbuf *lb, int row, int col)
212 char *ln = lbuf_get(lb, row);
213 return ln ? ren_off(ln, col) : 0;
216 static int vi_off2col(struct lbuf *lb, int row, int off)
218 char *ln = lbuf_get(lb, row);
219 return ln ? ren_pos(ln, off) : 0;
222 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
224 int o = *off + dir;
225 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
226 return 1;
227 *off = o;
228 return 0;
231 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
233 char *ln = lbuf_get(lb, *row);
234 int col = ln ? ren_pos(ln, *off) : 0;
235 int o = ln ? ren_next(ln, col, dir) : -1;
236 if (o < 0)
237 return -1;
238 *off = ren_off(ln, o);
239 return 0;
242 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
244 strcpy(vi_charlast, cs);
245 vi_charcmd = cmd;
246 return lbuf_findchar(lb, cs, cmd, n, row, off);
249 static int vi_search(int cmd, int cnt, int *row, int *off)
251 char *kwd;
252 int r = *row;
253 int o = *off;
254 int failed = 0;
255 int len = 0;
256 int i, dir;
257 if (cmd == '/' || cmd == '?') {
258 char sign[4] = {cmd};
259 struct sbuf *sb;
260 char *kw = vi_prompt(sign, ex_kmap());
261 char *re;
262 if (!kw)
263 return 1;
264 sb = sbuf_make();
265 sbuf_chr(sb, cmd);
266 sbuf_str(sb, kw);
267 free(kw);
268 kw = sbuf_buf(sb);
269 if ((re = re_read(&kw))) {
270 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
271 while (isspace(*kw))
272 kw++;
273 vi_soset = !!kw[0];
274 vi_so = atoi(kw);
275 free(re);
277 sbuf_free(sb);
279 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
280 return 1;
281 dir = cmd == 'N' ? -dir : dir;
282 o = *off;
283 for (i = 0; i < cnt; i++) {
284 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
285 failed = 1;
286 break;
288 if (i + 1 < cnt && cmd == '/')
289 o += len;
291 if (!failed) {
292 *row = r;
293 *off = o;
294 if (vi_soset) {
295 *off = -1;
296 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
297 failed = 1;
298 else
299 *row += vi_so;
302 if (failed)
303 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", kwd);
304 return failed;
307 /* read a line motion */
308 static int vi_motionln(int *row, int cmd)
310 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
311 int c = vi_read();
312 int mark, mark_row, mark_off;
313 switch (c) {
314 case '\n':
315 case '+':
316 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
317 break;
318 case '-':
319 *row = MAX(*row - cnt, 0);
320 break;
321 case '_':
322 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
323 break;
324 case '\'':
325 if ((mark = vi_read()) <= 0)
326 return -1;
327 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
328 return -1;
329 *row = mark_row;
330 break;
331 case 'j':
332 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
333 break;
334 case 'k':
335 *row = MAX(*row - cnt, 0);
336 break;
337 case 'G':
338 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
339 break;
340 case 'H':
341 if (lbuf_len(xb))
342 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
343 else
344 *row = 0;
345 break;
346 case 'L':
347 if (lbuf_len(xb))
348 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
349 else
350 *row = 0;
351 break;
352 case 'M':
353 if (lbuf_len(xb))
354 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
355 else
356 *row = 0;
357 break;
358 default:
359 if (c == cmd) {
360 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
361 break;
363 if (c == '%' && (vi_arg1 || vi_arg2)) {
364 if (cnt > 100)
365 return -1;
366 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
367 break;
369 vi_back(c);
370 return 0;
372 return c;
375 static char *vi_curword(struct lbuf *lb, int row, int off)
377 struct sbuf *sb;
378 char *ln = lbuf_get(lb, row);
379 char *beg, *end;
380 if (!ln)
381 return NULL;
382 beg = uc_chr(ln, ren_noeol(ln, off));
383 end = beg;
384 while (*end && uc_kind(end) == 1)
385 end = uc_next(end);
386 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
387 beg = uc_beg(ln, beg - 1);
388 if (beg >= end)
389 return NULL;
390 sb = sbuf_make();
391 sbuf_str(sb, "\\<");
392 sbuf_mem(sb, beg, end - beg);
393 sbuf_str(sb, "\\>");
394 return sbuf_done(sb);
397 /* read a motion */
398 static int vi_motion(int *row, int *off)
400 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
401 char *ln = lbuf_get(xb, *row);
402 int dir = dir_context(ln ? ln : "");
403 int mark, mark_row, mark_off;
404 char *cs;
405 int mv;
406 int i;
407 if ((mv = vi_motionln(row, 0))) {
408 *off = -1;
409 return mv;
411 mv = vi_read();
412 switch (mv) {
413 case 'f':
414 if (!(cs = vi_char()))
415 return -1;
416 if (vi_findchar(xb, cs, mv, cnt, row, off))
417 return -1;
418 break;
419 case 'F':
420 if (!(cs = vi_char()))
421 return -1;
422 if (vi_findchar(xb, cs, mv, cnt, row, off))
423 return -1;
424 break;
425 case ';':
426 if (!vi_charlast[0])
427 return -1;
428 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
429 return -1;
430 break;
431 case ',':
432 if (!vi_charlast[0])
433 return -1;
434 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
435 return -1;
436 break;
437 case 'h':
438 for (i = 0; i < cnt; i++)
439 if (vi_nextcol(xb, -1 * dir, row, off))
440 break;
441 break;
442 case 'l':
443 for (i = 0; i < cnt; i++)
444 if (vi_nextcol(xb, +1 * dir, row, off))
445 break;
446 break;
447 case 't':
448 if (!(cs = vi_char()))
449 return -1;
450 if (vi_findchar(xb, cs, mv, cnt, row, off))
451 return -1;
452 break;
453 case 'T':
454 if (!(cs = vi_char()))
455 return -1;
456 if (vi_findchar(xb, cs, mv, cnt, row, off))
457 return -1;
458 break;
459 case 'B':
460 for (i = 0; i < cnt; i++)
461 if (lbuf_wordend(xb, 1, -1, row, off))
462 break;
463 break;
464 case 'E':
465 for (i = 0; i < cnt; i++)
466 if (lbuf_wordend(xb, 1, +1, row, off))
467 break;
468 break;
469 case 'W':
470 for (i = 0; i < cnt; i++)
471 if (lbuf_wordbeg(xb, 1, +1, row, off))
472 break;
473 break;
474 case 'b':
475 for (i = 0; i < cnt; i++)
476 if (lbuf_wordend(xb, 0, -1, row, off))
477 break;
478 break;
479 case 'e':
480 for (i = 0; i < cnt; i++)
481 if (lbuf_wordend(xb, 0, +1, row, off))
482 break;
483 break;
484 case 'w':
485 for (i = 0; i < cnt; i++)
486 if (lbuf_wordbeg(xb, 0, +1, row, off))
487 break;
488 break;
489 case '{':
490 for (i = 0; i < cnt; i++)
491 if (lbuf_paragraphbeg(xb, -1, row, off))
492 break;
493 break;
494 case '}':
495 for (i = 0; i < cnt; i++)
496 if (lbuf_paragraphbeg(xb, +1, row, off))
497 break;
498 break;
499 case '[':
500 if (vi_read() != '[')
501 return -1;
502 for (i = 0; i < cnt; i++)
503 if (lbuf_sectionbeg(xb, -1, row, off))
504 break;
505 break;
506 case ']':
507 if (vi_read() != ']')
508 return -1;
509 for (i = 0; i < cnt; i++)
510 if (lbuf_sectionbeg(xb, +1, row, off))
511 break;
512 break;
513 case '0':
514 *off = 0;
515 break;
516 case '^':
517 *off = lbuf_indents(xb, *row);
518 break;
519 case '$':
520 *off = lbuf_eol(xb, *row);
521 break;
522 case '|':
523 *off = vi_col2off(xb, *row, cnt - 1);
524 vi_pcol = cnt - 1;
525 break;
526 case '/':
527 if (vi_search(mv, cnt, row, off))
528 return -1;
529 break;
530 case '?':
531 if (vi_search(mv, cnt, row, off))
532 return -1;
533 break;
534 case 'n':
535 if (vi_search(mv, cnt, row, off))
536 return -1;
537 break;
538 case 'N':
539 if (vi_search(mv, cnt, row, off))
540 return -1;
541 break;
542 case TK_CTL('a'):
543 if (!(cs = vi_curword(xb, *row, *off)))
544 return -1;
545 ex_kwdset(cs, +1);
546 free(cs);
547 if (vi_search('n', cnt, row, off))
548 return -1;
549 break;
550 case ' ':
551 for (i = 0; i < cnt; i++)
552 if (vi_nextoff(xb, +1, row, off))
553 break;
554 break;
555 case 127:
556 case TK_CTL('h'):
557 for (i = 0; i < cnt; i++)
558 if (vi_nextoff(xb, -1, row, off))
559 break;
560 break;
561 case '`':
562 if ((mark = vi_read()) <= 0)
563 return -1;
564 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
565 return -1;
566 *row = mark_row;
567 *off = mark_off;
568 break;
569 case '%':
570 if (lbuf_pair(xb, row, off))
571 return -1;
572 break;
573 default:
574 vi_back(mv);
575 return 0;
577 return mv;
580 static void swap(int *a, int *b)
582 int t = *a;
583 *a = *b;
584 *b = t;
587 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
589 struct sbuf *sb;
590 char *s1, *s2, *s3;
591 if (r1 == r2)
592 return uc_sub(lbuf_get(lb, r1), o1, o2);
593 sb = sbuf_make();
594 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
595 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
596 s2 = lbuf_cp(lb, r1 + 1, r2);
597 sbuf_str(sb, s1);
598 sbuf_str(sb, s2);
599 sbuf_str(sb, s3);
600 free(s1);
601 free(s2);
602 free(s3);
603 return sbuf_done(sb);
606 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
608 char *region;
609 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
610 reg_put(vi_ybuf, region, lnmode);
611 free(region);
612 xrow = r1;
613 xoff = lnmode ? xoff : o1;
616 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
618 char *pref, *post;
619 char *region;
620 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
621 reg_put(vi_ybuf, region, lnmode);
622 free(region);
623 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
624 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
625 if (!lnmode) {
626 struct sbuf *sb = sbuf_make();
627 sbuf_str(sb, pref);
628 sbuf_str(sb, post);
629 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
630 sbuf_free(sb);
631 } else {
632 lbuf_edit(xb, NULL, r1, r2 + 1);
634 xrow = r1;
635 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
636 free(pref);
637 free(post);
640 static int linecount(char *s)
642 int n;
643 for (n = 0; s; n++)
644 if ((s = strchr(s, '\n')))
645 s++;
646 return n;
649 static int charcount(char *text, char *post)
651 int tlen = strlen(text);
652 int plen = strlen(post);
653 char *nl = text;
654 int i;
655 if (tlen < plen)
656 return 0;
657 for (i = 0; i < tlen - plen; i++)
658 if (text[i] == '\n')
659 nl = text + i + 1;
660 return uc_slen(nl) - uc_slen(post);
663 static char *vi_input(char *pref, char *post, int *row, int *off)
665 char *rep = led_input(pref, post, ex_kmap());
666 if (!rep)
667 return NULL;
668 *row = linecount(rep) - 1;
669 *off = charcount(rep, post) - 1;
670 if (*off < 0)
671 *off = 0;
672 return rep;
675 static char *vi_indents(char *ln)
677 struct sbuf *sb = sbuf_make();
678 while (xai && ln && (*ln == ' ' || *ln == '\t'))
679 sbuf_chr(sb, *ln++);
680 return sbuf_done(sb);
683 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
685 char *region;
686 int row, off;
687 char *rep;
688 char *pref, *post;
689 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
690 reg_put(vi_ybuf, region, lnmode);
691 free(region);
692 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
693 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
694 vi_drawrm(r1, r2, 0);
695 rep = vi_input(pref, post, &row, &off);
696 if (rep) {
697 lbuf_edit(xb, rep, r1, r2 + 1);
698 xrow = r1 + row - 1;
699 xoff = off;
700 free(rep);
702 free(pref);
703 free(post);
706 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
708 char *pref, *post;
709 char *region, *s;
710 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
711 s = region;
712 while (*s) {
713 int c = (unsigned char) s[0];
714 if (c <= 0x7f) {
715 if (cmd == 'u')
716 s[0] = tolower(c);
717 if (cmd == 'U')
718 s[0] = toupper(c);
719 if (cmd == '~')
720 s[0] = islower(c) ? toupper(c) : tolower(c);
722 s = uc_next(s);
724 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
725 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
726 if (!lnmode) {
727 struct sbuf *sb = sbuf_make();
728 sbuf_str(sb, pref);
729 sbuf_str(sb, region);
730 sbuf_str(sb, post);
731 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
732 sbuf_free(sb);
733 } else {
734 lbuf_edit(xb, region, r1, r2 + 1);
736 xrow = r2;
737 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
738 free(region);
739 free(pref);
740 free(post);
743 static void vi_pipe(int r1, int r2)
745 char *text;
746 char *rep;
747 char *kmap = NULL;
748 char *cmd = vi_prompt("!", &kmap);
749 if (!cmd)
750 return;
751 text = lbuf_cp(xb, r1, r2 + 1);
752 rep = cmd_pipe(cmd, text, 1, 1);
753 if (rep)
754 lbuf_edit(xb, rep, r1, r2 + 1);
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 for (i = r1; i <= r2; i++) {
766 if (!(ln = lbuf_get(xb, i)))
767 continue;
768 sb = sbuf_make();
769 if (dir > 0)
770 sbuf_chr(sb, '\t');
771 else
772 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
773 sbuf_str(sb, ln);
774 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
775 sbuf_free(sb);
777 xrow = r1;
778 xoff = lbuf_indents(xb, xrow);
781 static int vc_motion(int cmd)
783 int r1 = xrow, r2 = xrow; /* region rows */
784 int o1 = xoff, o2 = xoff; /* visual region columns */
785 int lnmode = 0; /* line-based region */
786 int mv;
787 vi_arg2 = vi_prefix();
788 if (vi_arg2 < 0)
789 return 1;
790 o1 = ren_noeol(lbuf_get(xb, r1), o1);
791 o2 = o1;
792 if ((mv = vi_motionln(&r2, cmd))) {
793 o2 = -1;
794 } else if (!(mv = vi_motion(&r2, &o2))) {
795 vi_read();
796 return 1;
798 if (mv < 0)
799 return 1;
800 lnmode = o2 < 0;
801 if (lnmode) {
802 o1 = 0;
803 o2 = lbuf_eol(xb, r2);
805 if (r1 > r2) {
806 swap(&r1, &r2);
807 swap(&o1, &o2);
809 if (r1 == r2 && o1 > o2)
810 swap(&o1, &o2);
811 o1 = ren_noeol(lbuf_get(xb, r1), o1);
812 if (!lnmode && strchr("fFtTeE%", mv))
813 if (o2 < lbuf_eol(xb, r2))
814 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
815 if (cmd == 'y')
816 vi_yank(r1, o1, r2, o2, lnmode);
817 if (cmd == 'd')
818 vi_delete(r1, o1, r2, o2, lnmode);
819 if (cmd == 'c')
820 vi_change(r1, o1, r2, o2, lnmode);
821 if (cmd == '~' || cmd == 'u' || cmd == 'U')
822 vi_case(r1, o1, r2, o2, lnmode, cmd);
823 if (cmd == '!')
824 vi_pipe(r1, r2);
825 if (cmd == '>' || cmd == '<')
826 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
827 return 0;
830 static int vc_insert(int cmd)
832 char *pref, *post;
833 char *ln = lbuf_get(xb, xrow);
834 int row, off = 0;
835 char *rep;
836 if (cmd == 'I')
837 xoff = lbuf_indents(xb, xrow);
838 if (cmd == 'A')
839 xoff = lbuf_eol(xb, xrow);
840 xoff = ren_noeol(ln, xoff);
841 if (cmd == 'o')
842 xrow += 1;
843 if (cmd == 'i' || cmd == 'I')
844 off = xoff;
845 if (cmd == 'a' || cmd == 'A')
846 off = xoff + 1;
847 if (ln && ln[0] == '\n')
848 off = 0;
849 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
850 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
851 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
852 rep = vi_input(pref, post, &row, &off);
853 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
854 lbuf_edit(xb, "\n", 0, 0);
855 if (rep) {
856 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
857 xrow += row - 1;
858 xoff = off;
859 free(rep);
861 free(pref);
862 free(post);
863 return !rep;
866 static int vc_put(int cmd)
868 int cnt = MAX(1, vi_arg1);
869 int lnmode;
870 char *buf = reg_get(vi_ybuf, &lnmode);
871 int i;
872 if (!buf) {
873 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
874 return 1;
876 if (lnmode) {
877 struct sbuf *sb = sbuf_make();
878 for (i = 0; i < cnt; i++)
879 sbuf_str(sb, buf);
880 if (!lbuf_len(xb))
881 lbuf_edit(xb, "\n", 0, 0);
882 if (cmd == 'p')
883 xrow++;
884 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
885 xoff = lbuf_indents(xb, xrow);
886 sbuf_free(sb);
887 } else {
888 struct sbuf *sb = sbuf_make();
889 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
890 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
891 char *s = uc_sub(ln, 0, off);
892 sbuf_str(sb, s);
893 free(s);
894 for (i = 0; i < cnt; i++)
895 sbuf_str(sb, buf);
896 s = uc_sub(ln, off, -1);
897 sbuf_str(sb, s);
898 free(s);
899 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
900 xoff = off + uc_slen(buf) * cnt - 1;
901 sbuf_free(sb);
903 return 0;
906 static int join_spaces(char *prev, char *next)
908 int prevlen = strlen(prev);
909 if (!prev[0])
910 return 0;
911 if (prev[prevlen - 1] == ' ' || next[0] == ')')
912 return 0;
913 return prev[prevlen - 1] == '.' ? 2 : 1;
916 static int vc_join(void)
918 struct sbuf *sb;
919 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
920 int beg = xrow;
921 int end = xrow + cnt;
922 int off = 0;
923 int i;
924 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
925 return 1;
926 sb = sbuf_make();
927 for (i = beg; i < end; i++) {
928 char *ln = lbuf_get(xb, i);
929 char *lnend = strchr(ln, '\n');
930 int spaces;
931 if (i > beg)
932 while (ln[0] == ' ' || ln[0] == '\t')
933 ln++;
934 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
935 off = uc_slen(sbuf_buf(sb));
936 while (spaces--)
937 sbuf_chr(sb, ' ');
938 sbuf_mem(sb, ln, lnend - ln);
940 sbuf_chr(sb, '\n');
941 lbuf_edit(xb, sbuf_buf(sb), beg, end);
942 xoff = off;
943 sbuf_free(sb);
944 return 0;
947 static int vi_scrollforeward(int cnt)
949 if (xtop >= lbuf_len(xb) - 1)
950 return 1;
951 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
952 xrow = MAX(xrow, xtop);
953 return 0;
956 static int vi_scrollbackward(int cnt)
958 if (xtop == 0)
959 return 1;
960 xtop = MAX(0, xtop - cnt);
961 xrow = MIN(xrow, xtop + xrows - 1);
962 return 0;
965 static void vc_status(void)
967 int col = vi_off2col(xb, xrow, xoff);
968 snprintf(vi_msg, sizeof(vi_msg),
969 "\"%s\"%c %d lines L%d C%d\n",
970 ex_path()[0] ? ex_path() : "unnamed",
971 lbuf_modified(xb) ? '*' : ' ',
972 lbuf_len(xb), xrow + 1,
973 ren_cursor(lbuf_get(xb, xrow), col) + 1);
976 static void vc_charinfo(void)
978 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
979 if (c) {
980 char cbuf[8] = "";
981 memcpy(cbuf, c, uc_len(c));
982 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
986 static int vc_replace(void)
988 int cnt = MAX(1, vi_arg1);
989 char *cs = vi_char();
990 char *ln = lbuf_get(xb, xrow);
991 struct sbuf *sb;
992 char *pref, *post;
993 char *s;
994 int off, i;
995 if (!ln || !cs)
996 return 1;
997 off = ren_noeol(ln, xoff);
998 s = uc_chr(ln, off);
999 for (i = 0; s[0] != '\n' && i < cnt; i++)
1000 s = uc_next(s);
1001 if (i < cnt)
1002 return 1;
1003 pref = uc_sub(ln, 0, off);
1004 post = uc_sub(ln, off + cnt, -1);
1005 sb = sbuf_make();
1006 sbuf_str(sb, pref);
1007 for (i = 0; i < cnt; i++)
1008 sbuf_str(sb, cs);
1009 sbuf_str(sb, post);
1010 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1011 off += cnt - 1;
1012 xoff = off;
1013 sbuf_free(sb);
1014 free(pref);
1015 free(post);
1016 return 0;
1019 static char rep_cmd[4096]; /* the last command */
1020 static int rep_len;
1022 static void vc_repeat(void)
1024 int i;
1025 for (i = 0; i < MAX(1, vi_arg1); i++)
1026 term_push(rep_cmd, rep_len);
1029 static void vc_execute(void)
1031 static int exec_buf;
1032 int lnmode;
1033 int c = vi_read();
1034 char *buf;
1035 int i;
1036 if (TK_INT(c))
1037 return;
1038 if (c == '@')
1039 c = exec_buf;
1040 exec_buf = c;
1041 buf = reg_get(exec_buf, &lnmode);
1042 if (buf)
1043 for (i = 0; i < MAX(1, vi_arg1); i++)
1044 term_push(buf, strlen(buf));
1047 static void vi(void)
1049 int xcol;
1050 int mark;
1051 char *ln;
1052 char *kmap = NULL;
1053 xtop = MAX(0, xrow - xrows / 2);
1054 xoff = 0;
1055 xcol = vi_off2col(xb, xrow, xoff);
1056 vi_drawagain(xcol, 0);
1057 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1058 while (!xquit) {
1059 int mod = 0; /* screen should be redrawn */
1060 int nrow = xrow;
1061 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1062 int otop = xtop;
1063 int oleft = xleft;
1064 int mv, n;
1065 term_cmd(&n);
1066 vi_arg2 = 0;
1067 vi_ybuf = vi_yankbuf();
1068 vi_arg1 = vi_prefix();
1069 if (!vi_ybuf)
1070 vi_ybuf = vi_yankbuf();
1071 mv = vi_motion(&nrow, &noff);
1072 if (mv > 0) {
1073 if (strchr("\'`GHML/?{}[]nN", mv) ||
1074 (mv == '%' && noff < 0)) {
1075 lbuf_mark(xb, '\'', xrow, xoff);
1076 lbuf_mark(xb, '`', xrow, xoff);
1078 xrow = nrow;
1079 if (noff < 0 && !strchr("jk", mv))
1080 noff = lbuf_indents(xb, xrow);
1081 if (strchr("jk", mv))
1082 noff = vi_col2off(xb, xrow, xcol);
1083 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1084 if (!strchr("|jk", mv))
1085 xcol = vi_off2col(xb, xrow, xoff);
1086 if (mv == '|')
1087 xcol = vi_pcol;
1088 } else if (mv == 0) {
1089 char *cmd;
1090 int c = vi_read();
1091 int k;
1092 if (c <= 0)
1093 continue;
1094 lbuf_mark(xb, '*', xrow, xoff);
1095 switch (c) {
1096 case TK_CTL('b'):
1097 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1098 break;
1099 xoff = lbuf_indents(xb, xrow);
1100 break;
1101 case TK_CTL('f'):
1102 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1103 break;
1104 xoff = lbuf_indents(xb, xrow);
1105 break;
1106 case TK_CTL('e'):
1107 if (vi_scrollforeward(MAX(1, vi_arg1)))
1108 break;
1109 break;
1110 case TK_CTL('y'):
1111 if (vi_scrollbackward(MAX(1, vi_arg1)))
1112 break;
1113 break;
1114 case TK_CTL('u'):
1115 if (xrow == 0)
1116 break;
1117 if (vi_arg1)
1118 vi_scroll = vi_arg1;
1119 n = vi_scroll ? vi_scroll : xrows / 2;
1120 xrow = MAX(0, xrow - n);
1121 if (xtop > 0)
1122 xtop = MAX(0, xtop - n);
1123 xoff = lbuf_indents(xb, xrow);
1124 break;
1125 case TK_CTL('d'):
1126 if (xrow == lbuf_len(xb) - 1)
1127 break;
1128 if (vi_arg1)
1129 vi_scroll = vi_arg1;
1130 n = vi_scroll ? vi_scroll : xrows / 2;
1131 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1132 if (xtop < lbuf_len(xb) - xrows)
1133 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1134 xoff = lbuf_indents(xb, xrow);
1135 break;
1136 case TK_CTL('z'):
1137 term_pos(xrows, 0);
1138 term_suspend();
1139 mod = 1;
1140 break;
1141 case 'u':
1142 if (!lbuf_undo(xb)) {
1143 lbuf_jump(xb, '*', &xrow, &xoff);
1144 mod = 1;
1145 } else {
1146 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1148 break;
1149 case TK_CTL('r'):
1150 if (!lbuf_redo(xb)) {
1151 lbuf_jump(xb, '*', &xrow, &xoff);
1152 mod = 1;
1153 } else {
1154 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1156 break;
1157 case TK_CTL('g'):
1158 vc_status();
1159 break;
1160 case TK_CTL('^'):
1161 ex_command("e #");
1162 mod = 1;
1163 break;
1164 case ':':
1165 ln = vi_prompt(":", &kmap);
1166 if (ln && ln[0]) {
1167 ex_command(ln);
1168 mod = 1;
1170 free(ln);
1171 if (xquit)
1172 continue;
1173 break;
1174 case 'c':
1175 case 'd':
1176 case 'y':
1177 case '!':
1178 case '>':
1179 case '<':
1180 if (!vc_motion(c))
1181 mod = 1;
1182 break;
1183 case 'i':
1184 case 'I':
1185 case 'a':
1186 case 'A':
1187 case 'o':
1188 case 'O':
1189 if (!vc_insert(c))
1190 mod = 1;
1191 break;
1192 case 'J':
1193 if (!vc_join())
1194 mod = 1;
1195 break;
1196 case TK_CTL('l'):
1197 term_done();
1198 term_init();
1199 mod = 1;
1200 break;
1201 case 'm':
1202 if ((mark = vi_read()) > 0 && islower(mark))
1203 lbuf_mark(xb, mark, xrow, xoff);
1204 break;
1205 case 'p':
1206 case 'P':
1207 if (!vc_put(c))
1208 mod = 1;
1209 break;
1210 case 'z':
1211 k = vi_read();
1212 switch (k) {
1213 case '\n':
1214 xtop = vi_arg1 ? vi_arg1 : xrow;
1215 break;
1216 case '.':
1217 n = vi_arg1 ? vi_arg1 : xrow;
1218 xtop = MAX(0, n - xrows / 2);
1219 break;
1220 case '-':
1221 n = vi_arg1 ? vi_arg1 : xrow;
1222 xtop = MAX(0, n - xrows + 1);
1223 break;
1224 case 'l':
1225 case 'r':
1226 xdir = k == 'r' ? -1 : +1;
1227 break;
1228 case 'L':
1229 case 'R':
1230 xdir = k == 'R' ? -2 : +2;
1231 break;
1233 mod = 1;
1234 break;
1235 case 'g':
1236 k = vi_read();
1237 if (k == '~' || k == 'u' || k == 'U')
1238 if (!vc_motion(k))
1239 mod = 1;
1240 if (k == 'a')
1241 vc_charinfo();
1242 break;
1243 case 'x':
1244 vi_back(' ');
1245 if (!vc_motion('d'))
1246 mod = 2;
1247 break;
1248 case 'X':
1249 vi_back(TK_CTL('h'));
1250 if (!vc_motion('d'))
1251 mod = 2;
1252 break;
1253 case 'C':
1254 vi_back('$');
1255 if (!vc_motion('c'))
1256 mod = 1;
1257 break;
1258 case 'D':
1259 vi_back('$');
1260 if (!vc_motion('d'))
1261 mod = 2;
1262 break;
1263 case 'r':
1264 if (!vc_replace())
1265 mod = 2;
1266 break;
1267 case 's':
1268 vi_back(' ');
1269 if (!vc_motion('c'))
1270 mod = 1;
1271 break;
1272 case 'S':
1273 vi_back('c');
1274 if (!vc_motion('c'))
1275 mod = 1;
1276 break;
1277 case 'Y':
1278 vi_back('y');
1279 vc_motion('y');
1280 break;
1281 case 'Z':
1282 k = vi_read();
1283 if (k == 'Z')
1284 ex_command("x");
1285 break;
1286 case '~':
1287 vi_back(' ');
1288 if (!vc_motion('~'))
1289 mod = 2;
1290 break;
1291 case '.':
1292 vc_repeat();
1293 break;
1294 case '@':
1295 vc_execute();
1296 break;
1297 default:
1298 continue;
1300 cmd = term_cmd(&n);
1301 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1302 if (n < sizeof(rep_cmd)) {
1303 memcpy(rep_cmd, cmd, n);
1304 rep_len = n;
1308 if (xrow < 0 || xrow >= lbuf_len(xb))
1309 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1310 if (xtop > xrow)
1311 xtop = xtop - xrows / 2 > xrow ?
1312 MAX(0, xrow - xrows / 2) : xrow;
1313 if (xtop + xrows <= xrow)
1314 xtop = xtop + xrows + xrows / 2 <= xrow ?
1315 xrow - xrows / 2 : xrow - xrows + 1;
1316 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1317 if (mod)
1318 xcol = vi_off2col(xb, xrow, xoff);
1319 if (xcol >= xleft + xcols)
1320 xleft = xcol - xcols / 2;
1321 if (xcol < xleft)
1322 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1323 vi_wait();
1324 if (mod || xleft != oleft)
1325 vi_drawagain(xcol, mod == 2 && xleft == oleft);
1326 else if (xtop != otop)
1327 vi_drawupdate(xcol, otop);
1328 if (vi_msg[0])
1329 vi_drawmsg();
1330 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1331 ren_cursor(lbuf_get(xb, xrow), xcol)));
1332 lbuf_modified(xb);
1334 term_pos(xrows, 0);
1335 term_kill();
1338 int main(int argc, char *argv[])
1340 int i;
1341 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1342 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1343 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1344 if (argv[i][1] == 's')
1345 xled = 0;
1346 if (argv[i][1] == 'e')
1347 xvis = 0;
1348 if (argv[i][1] == 'v')
1349 xvis = 1;
1351 dir_init();
1352 syn_init();
1353 if (xled || xvis)
1354 term_init();
1355 if (!ex_init(argv + i)) {
1356 if (xvis)
1357 vi();
1358 else
1359 ex();
1360 if (xled || xvis)
1361 term_done();
1362 ex_done();
1364 reg_done();
1365 syn_done();
1366 dir_done();
1367 return 0;