vi: redraw the entire screen if current line changes
[neatvi.git] / vi.c
blobde99bec4c7d6c070f4b48dd4ce8f02faab287c56
1 /*
2 * NEATVI Editor
4 * Copyright (C) 2015-2019 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_printmsg(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, ex_filetype());
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 = 0;
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(&xkmap);
124 static char *vi_prompt(char *msg, int *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 int oleft = xleft;
144 char *s = led_prompt(msg, "", &xkmap, "---");
145 xleft = oleft;
146 if (s)
147 term_chr('\n');
148 return s;
150 sb = sbuf_make();
151 while ((c = getchar()) != EOF && c != '\n')
152 sbuf_chr(sb, c);
153 if (c == EOF) {
154 sbuf_free(sb);
155 return NULL;
157 return sbuf_done(sb);
160 /* show an ex message */
161 void ex_show(char *msg)
163 if (xvis) {
164 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
165 } else if (xled) {
166 led_print(msg, -1, "---");
167 term_chr('\n');
168 } else {
169 printf("%s", msg);
173 /* print an ex output line */
174 void ex_print(char *line)
176 if (xvis) {
177 vi_printed += line ? 1 : 2;
178 if (line)
179 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
180 if (line)
181 led_print(line, -1, "");
182 term_chr('\n');
183 } else {
184 if (line)
185 ex_show(line);
189 static int vi_yankbuf(void)
191 int c = vi_read();
192 if (c == '"')
193 return vi_read();
194 vi_back(c);
195 return 0;
198 static int vi_prefix(void)
200 int n = 0;
201 int c = vi_read();
202 if ((c >= '1' && c <= '9')) {
203 while (isdigit(c)) {
204 n = n * 10 + c - '0';
205 c = vi_read();
208 vi_back(c);
209 return n;
212 static int vi_col2off(struct lbuf *lb, int row, int col)
214 char *ln = lbuf_get(lb, row);
215 return ln ? ren_off(ln, col) : 0;
218 static int vi_off2col(struct lbuf *lb, int row, int off)
220 char *ln = lbuf_get(lb, row);
221 return ln ? ren_pos(ln, off) : 0;
224 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
226 int o = *off + dir;
227 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
228 return 1;
229 *off = o;
230 return 0;
233 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
235 char *ln = lbuf_get(lb, *row);
236 int col = ln ? ren_pos(ln, *off) : 0;
237 int o = ln ? ren_next(ln, col, dir) : -1;
238 if (o < 0)
239 return -1;
240 *off = ren_off(ln, o);
241 return 0;
244 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
246 strcpy(vi_charlast, cs);
247 vi_charcmd = cmd;
248 return lbuf_findchar(lb, cs, cmd, n, row, off);
251 static int vi_search(int cmd, int cnt, int *row, int *off)
253 char *kwd;
254 int r = *row;
255 int o = *off;
256 int failed = 0;
257 int len = 0;
258 int i, dir;
259 if (cmd == '/' || cmd == '?') {
260 char sign[4] = {cmd};
261 struct sbuf *sb;
262 char *kw = vi_prompt(sign, &xkmap);
263 char *re;
264 if (!kw)
265 return 1;
266 sb = sbuf_make();
267 sbuf_chr(sb, cmd);
268 sbuf_str(sb, kw);
269 free(kw);
270 kw = sbuf_buf(sb);
271 if ((re = re_read(&kw))) {
272 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
273 while (isspace(*kw))
274 kw++;
275 vi_soset = !!kw[0];
276 vi_so = atoi(kw);
277 free(re);
279 sbuf_free(sb);
281 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
282 return 1;
283 dir = cmd == 'N' ? -dir : dir;
284 o = *off;
285 for (i = 0; i < cnt; i++) {
286 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
287 failed = 1;
288 break;
290 if (i + 1 < cnt && cmd == '/')
291 o += len;
293 if (!failed) {
294 *row = r;
295 *off = o;
296 if (vi_soset) {
297 *off = -1;
298 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
299 failed = 1;
300 else
301 *row += vi_so;
304 if (failed)
305 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", kwd);
306 return failed;
309 /* read a line motion */
310 static int vi_motionln(int *row, int cmd)
312 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
313 int c = vi_read();
314 int mark, mark_row, mark_off;
315 switch (c) {
316 case '\n':
317 case '+':
318 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
319 break;
320 case '-':
321 *row = MAX(*row - cnt, 0);
322 break;
323 case '_':
324 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
325 break;
326 case '\'':
327 if ((mark = vi_read()) <= 0)
328 return -1;
329 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
330 return -1;
331 *row = mark_row;
332 break;
333 case 'j':
334 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
335 break;
336 case 'k':
337 *row = MAX(*row - cnt, 0);
338 break;
339 case 'G':
340 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
341 break;
342 case 'H':
343 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
344 break;
345 case 'L':
346 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
347 break;
348 case 'M':
349 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
350 break;
351 default:
352 if (c == cmd) {
353 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
354 break;
356 if (c == '%' && (vi_arg1 || vi_arg2)) {
357 if (cnt > 100)
358 return -1;
359 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
360 break;
362 vi_back(c);
363 return 0;
365 if (*row < 0)
366 *row = 0;
367 return c;
370 static char *vi_curword(struct lbuf *lb, int row, int off)
372 struct sbuf *sb;
373 char *ln = lbuf_get(lb, row);
374 char *beg, *end;
375 if (!ln)
376 return NULL;
377 beg = uc_chr(ln, ren_noeol(ln, off));
378 end = beg;
379 while (*end && uc_kind(end) == 1)
380 end = uc_next(end);
381 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
382 beg = uc_beg(ln, beg - 1);
383 if (beg >= end)
384 return NULL;
385 sb = sbuf_make();
386 sbuf_str(sb, "\\<");
387 sbuf_mem(sb, beg, end - beg);
388 sbuf_str(sb, "\\>");
389 return sbuf_done(sb);
392 /* read a motion */
393 static int vi_motion(int *row, int *off)
395 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
396 char *ln = lbuf_get(xb, *row);
397 int dir = dir_context(ln ? ln : "");
398 int mark, mark_row, mark_off;
399 char *cs;
400 int mv;
401 int i;
402 if ((mv = vi_motionln(row, 0))) {
403 *off = -1;
404 return mv;
406 mv = vi_read();
407 switch (mv) {
408 case 'f':
409 if (!(cs = vi_char()))
410 return -1;
411 if (vi_findchar(xb, cs, mv, cnt, row, off))
412 return -1;
413 break;
414 case 'F':
415 if (!(cs = vi_char()))
416 return -1;
417 if (vi_findchar(xb, cs, mv, cnt, row, off))
418 return -1;
419 break;
420 case ';':
421 if (!vi_charlast[0])
422 return -1;
423 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
424 return -1;
425 break;
426 case ',':
427 if (!vi_charlast[0])
428 return -1;
429 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
430 return -1;
431 break;
432 case 'h':
433 for (i = 0; i < cnt; i++)
434 if (vi_nextcol(xb, -1 * dir, row, off))
435 break;
436 break;
437 case 'l':
438 for (i = 0; i < cnt; i++)
439 if (vi_nextcol(xb, +1 * dir, row, off))
440 break;
441 break;
442 case 't':
443 if (!(cs = vi_char()))
444 return -1;
445 if (vi_findchar(xb, cs, mv, cnt, row, off))
446 return -1;
447 break;
448 case 'T':
449 if (!(cs = vi_char()))
450 return -1;
451 if (vi_findchar(xb, cs, mv, cnt, row, off))
452 return -1;
453 break;
454 case 'B':
455 for (i = 0; i < cnt; i++)
456 if (lbuf_wordend(xb, 1, -1, row, off))
457 break;
458 break;
459 case 'E':
460 for (i = 0; i < cnt; i++)
461 if (lbuf_wordend(xb, 1, +1, row, off))
462 break;
463 break;
464 case 'W':
465 for (i = 0; i < cnt; i++)
466 if (lbuf_wordbeg(xb, 1, +1, row, off))
467 break;
468 break;
469 case 'b':
470 for (i = 0; i < cnt; i++)
471 if (lbuf_wordend(xb, 0, -1, row, off))
472 break;
473 break;
474 case 'e':
475 for (i = 0; i < cnt; i++)
476 if (lbuf_wordend(xb, 0, +1, row, off))
477 break;
478 break;
479 case 'w':
480 for (i = 0; i < cnt; i++)
481 if (lbuf_wordbeg(xb, 0, +1, row, off))
482 break;
483 break;
484 case '{':
485 for (i = 0; i < cnt; i++)
486 if (lbuf_paragraphbeg(xb, -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 if (vi_read() != '[')
496 return -1;
497 for (i = 0; i < cnt; i++)
498 if (lbuf_sectionbeg(xb, -1, row, off))
499 break;
500 break;
501 case ']':
502 if (vi_read() != ']')
503 return -1;
504 for (i = 0; i < cnt; i++)
505 if (lbuf_sectionbeg(xb, +1, row, off))
506 break;
507 break;
508 case '0':
509 *off = 0;
510 break;
511 case '^':
512 *off = lbuf_indents(xb, *row);
513 break;
514 case '$':
515 *off = lbuf_eol(xb, *row);
516 break;
517 case '|':
518 *off = vi_col2off(xb, *row, cnt - 1);
519 vi_pcol = cnt - 1;
520 break;
521 case '/':
522 if (vi_search(mv, cnt, row, off))
523 return -1;
524 break;
525 case '?':
526 if (vi_search(mv, cnt, row, off))
527 return -1;
528 break;
529 case 'n':
530 if (vi_search(mv, cnt, row, off))
531 return -1;
532 break;
533 case 'N':
534 if (vi_search(mv, cnt, row, off))
535 return -1;
536 break;
537 case TK_CTL('a'):
538 if (!(cs = vi_curword(xb, *row, *off)))
539 return -1;
540 ex_kwdset(cs, +1);
541 free(cs);
542 if (vi_search('n', cnt, row, off))
543 return -1;
544 break;
545 case ' ':
546 for (i = 0; i < cnt; i++)
547 if (vi_nextoff(xb, +1, row, off))
548 break;
549 break;
550 case 127:
551 case TK_CTL('h'):
552 for (i = 0; i < cnt; i++)
553 if (vi_nextoff(xb, -1, row, off))
554 break;
555 break;
556 case '`':
557 if ((mark = vi_read()) <= 0)
558 return -1;
559 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
560 return -1;
561 *row = mark_row;
562 *off = mark_off;
563 break;
564 case '%':
565 if (lbuf_pair(xb, row, off))
566 return -1;
567 break;
568 default:
569 vi_back(mv);
570 return 0;
572 return mv;
575 static void swap(int *a, int *b)
577 int t = *a;
578 *a = *b;
579 *b = t;
582 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
584 struct sbuf *sb;
585 char *s1, *s2, *s3;
586 if (r1 == r2)
587 return uc_sub(lbuf_get(lb, r1), o1, o2);
588 sb = sbuf_make();
589 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
590 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
591 s2 = lbuf_cp(lb, r1 + 1, r2);
592 sbuf_str(sb, s1);
593 sbuf_str(sb, s2);
594 sbuf_str(sb, s3);
595 free(s1);
596 free(s2);
597 free(s3);
598 return sbuf_done(sb);
601 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
603 char *region;
604 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
605 reg_put(vi_ybuf, region, lnmode);
606 free(region);
607 xrow = r1;
608 xoff = lnmode ? xoff : o1;
611 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
613 char *pref, *post;
614 char *region;
615 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
616 reg_put(vi_ybuf, region, lnmode);
617 free(region);
618 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
619 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
620 if (!lnmode) {
621 struct sbuf *sb = sbuf_make();
622 sbuf_str(sb, pref);
623 sbuf_str(sb, post);
624 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
625 sbuf_free(sb);
626 } else {
627 lbuf_edit(xb, NULL, r1, r2 + 1);
629 xrow = r1;
630 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
631 free(pref);
632 free(post);
635 static int linecount(char *s)
637 int n;
638 for (n = 0; s; n++)
639 if ((s = strchr(s, '\n')))
640 s++;
641 return n;
644 static int charcount(char *text, char *post)
646 int tlen = strlen(text);
647 int plen = strlen(post);
648 char *nl = text;
649 int i;
650 if (tlen < plen)
651 return 0;
652 for (i = 0; i < tlen - plen; i++)
653 if (text[i] == '\n')
654 nl = text + i + 1;
655 return uc_slen(nl) - uc_slen(post);
658 static char *vi_input(char *pref, char *post, int *row, int *off)
660 char *rep = led_input(pref, post, &xkmap, ex_filetype());
661 if (!rep)
662 return NULL;
663 *row = linecount(rep) - 1;
664 *off = charcount(rep, post) - 1;
665 if (*off < 0)
666 *off = 0;
667 return rep;
670 static char *vi_indents(char *ln)
672 struct sbuf *sb = sbuf_make();
673 while (xai && ln && (*ln == ' ' || *ln == '\t'))
674 sbuf_chr(sb, *ln++);
675 return sbuf_done(sb);
678 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
680 char *region;
681 int row, off;
682 char *rep;
683 char *pref, *post;
684 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
685 reg_put(vi_ybuf, region, lnmode);
686 free(region);
687 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
688 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
689 vi_drawrm(r1, r2, 0);
690 rep = vi_input(pref, post, &row, &off);
691 if (rep) {
692 lbuf_edit(xb, rep, r1, r2 + 1);
693 xrow = r1 + row - 1;
694 xoff = off;
695 free(rep);
697 free(pref);
698 free(post);
701 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
703 char *pref, *post;
704 char *region, *s;
705 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
706 s = region;
707 while (*s) {
708 int c = (unsigned char) s[0];
709 if (c <= 0x7f) {
710 if (cmd == 'u')
711 s[0] = tolower(c);
712 if (cmd == 'U')
713 s[0] = toupper(c);
714 if (cmd == '~')
715 s[0] = islower(c) ? toupper(c) : tolower(c);
717 s = uc_next(s);
719 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
720 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
721 if (!lnmode) {
722 struct sbuf *sb = sbuf_make();
723 sbuf_str(sb, pref);
724 sbuf_str(sb, region);
725 sbuf_str(sb, post);
726 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
727 sbuf_free(sb);
728 } else {
729 lbuf_edit(xb, region, r1, r2 + 1);
731 xrow = r2;
732 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
733 free(region);
734 free(pref);
735 free(post);
738 static void vi_pipe(int r1, int r2)
740 char *text;
741 char *rep;
742 int kmap = 0;
743 char *cmd = vi_prompt("!", &kmap);
744 if (!cmd)
745 return;
746 text = lbuf_cp(xb, r1, r2 + 1);
747 rep = cmd_pipe(cmd, text, 1, 1);
748 if (rep)
749 lbuf_edit(xb, rep, r1, r2 + 1);
750 free(cmd);
751 free(text);
752 free(rep);
755 static void vi_shift(int r1, int r2, int dir)
757 struct sbuf *sb;
758 char *ln;
759 int i;
760 for (i = r1; i <= r2; i++) {
761 if (!(ln = lbuf_get(xb, i)))
762 continue;
763 sb = sbuf_make();
764 if (dir > 0)
765 sbuf_chr(sb, '\t');
766 else
767 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
768 sbuf_str(sb, ln);
769 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
770 sbuf_free(sb);
772 xrow = r1;
773 xoff = lbuf_indents(xb, xrow);
776 static int vc_motion(int cmd)
778 int r1 = xrow, r2 = xrow; /* region rows */
779 int o1 = xoff, o2 = xoff; /* visual region columns */
780 int lnmode = 0; /* line-based region */
781 int mv;
782 vi_arg2 = vi_prefix();
783 if (vi_arg2 < 0)
784 return 1;
785 o1 = ren_noeol(lbuf_get(xb, r1), o1);
786 o2 = o1;
787 if ((mv = vi_motionln(&r2, cmd))) {
788 o2 = -1;
789 } else if (!(mv = vi_motion(&r2, &o2))) {
790 vi_read();
791 return 1;
793 if (mv < 0)
794 return 1;
795 lnmode = o2 < 0;
796 if (lnmode) {
797 o1 = 0;
798 o2 = lbuf_eol(xb, r2);
800 if (r1 > r2) {
801 swap(&r1, &r2);
802 swap(&o1, &o2);
804 if (r1 == r2 && o1 > o2)
805 swap(&o1, &o2);
806 o1 = ren_noeol(lbuf_get(xb, r1), o1);
807 if (!lnmode && strchr("fFtTeE%", mv))
808 if (o2 < lbuf_eol(xb, r2))
809 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
810 if (cmd == 'y')
811 vi_yank(r1, o1, r2, o2, lnmode);
812 if (cmd == 'd')
813 vi_delete(r1, o1, r2, o2, lnmode);
814 if (cmd == 'c')
815 vi_change(r1, o1, r2, o2, lnmode);
816 if (cmd == '~' || cmd == 'u' || cmd == 'U')
817 vi_case(r1, o1, r2, o2, lnmode, cmd);
818 if (cmd == '!')
819 vi_pipe(r1, r2);
820 if (cmd == '>' || cmd == '<')
821 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
822 return 0;
825 static int vc_insert(int cmd)
827 char *pref, *post;
828 char *ln = lbuf_get(xb, xrow);
829 int row, off = 0;
830 char *rep;
831 if (cmd == 'I')
832 xoff = lbuf_indents(xb, xrow);
833 if (cmd == 'A')
834 xoff = lbuf_eol(xb, xrow);
835 xoff = ren_noeol(ln, xoff);
836 if (cmd == 'o')
837 xrow += 1;
838 if (cmd == 'i' || cmd == 'I')
839 off = xoff;
840 if (cmd == 'a' || cmd == 'A')
841 off = xoff + 1;
842 if (ln && ln[0] == '\n')
843 off = 0;
844 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
845 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
846 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
847 rep = vi_input(pref, post, &row, &off);
848 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
849 lbuf_edit(xb, "\n", 0, 0);
850 if (rep) {
851 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
852 xrow += row - 1;
853 xoff = off;
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 *buf = reg_get(vi_ybuf, &lnmode);
866 int i;
867 if (!buf) {
868 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
869 return 1;
871 if (lnmode) {
872 struct sbuf *sb = sbuf_make();
873 for (i = 0; i < cnt; i++)
874 sbuf_str(sb, buf);
875 if (!lbuf_len(xb))
876 lbuf_edit(xb, "\n", 0, 0);
877 if (cmd == 'p')
878 xrow++;
879 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
880 xoff = lbuf_indents(xb, xrow);
881 sbuf_free(sb);
882 } else {
883 struct sbuf *sb = sbuf_make();
884 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
885 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
886 char *s = uc_sub(ln, 0, off);
887 sbuf_str(sb, s);
888 free(s);
889 for (i = 0; i < cnt; i++)
890 sbuf_str(sb, buf);
891 s = uc_sub(ln, off, -1);
892 sbuf_str(sb, s);
893 free(s);
894 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
895 xoff = off + uc_slen(buf) * cnt - 1;
896 sbuf_free(sb);
898 return 0;
901 static int join_spaces(char *prev, char *next)
903 int prevlen = strlen(prev);
904 if (!prev[0])
905 return 0;
906 if (prev[prevlen - 1] == ' ' || next[0] == ')')
907 return 0;
908 return prev[prevlen - 1] == '.' ? 2 : 1;
911 static int vc_join(void)
913 struct sbuf *sb;
914 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
915 int beg = xrow;
916 int end = xrow + cnt;
917 int off = 0;
918 int i;
919 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
920 return 1;
921 sb = sbuf_make();
922 for (i = beg; i < end; i++) {
923 char *ln = lbuf_get(xb, i);
924 char *lnend = strchr(ln, '\n');
925 int spaces;
926 if (i > beg)
927 while (ln[0] == ' ' || ln[0] == '\t')
928 ln++;
929 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
930 off = uc_slen(sbuf_buf(sb));
931 while (spaces--)
932 sbuf_chr(sb, ' ');
933 sbuf_mem(sb, ln, lnend - ln);
935 sbuf_chr(sb, '\n');
936 lbuf_edit(xb, sbuf_buf(sb), beg, end);
937 xoff = off;
938 sbuf_free(sb);
939 return 0;
942 static int vi_scrollforeward(int cnt)
944 if (xtop >= lbuf_len(xb) - 1)
945 return 1;
946 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
947 xrow = MAX(xrow, xtop);
948 return 0;
951 static int vi_scrollbackward(int cnt)
953 if (xtop == 0)
954 return 1;
955 xtop = MAX(0, xtop - cnt);
956 xrow = MIN(xrow, xtop + xrows - 1);
957 return 0;
960 static void vc_status(void)
962 int col = vi_off2col(xb, xrow, xoff);
963 snprintf(vi_msg, sizeof(vi_msg),
964 "\"%s\"%c %d lines L%d C%d\n",
965 ex_path()[0] ? ex_path() : "unnamed",
966 lbuf_modified(xb) ? '*' : ' ',
967 lbuf_len(xb), xrow + 1,
968 ren_cursor(lbuf_get(xb, xrow), col) + 1);
971 static void vc_charinfo(void)
973 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
974 if (c) {
975 char cbuf[8] = "";
976 memcpy(cbuf, c, uc_len(c));
977 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
981 static int vc_replace(void)
983 int cnt = MAX(1, vi_arg1);
984 char *cs = vi_char();
985 char *ln = lbuf_get(xb, xrow);
986 struct sbuf *sb;
987 char *pref, *post;
988 char *s;
989 int off, i;
990 if (!ln || !cs)
991 return 1;
992 off = ren_noeol(ln, xoff);
993 s = uc_chr(ln, off);
994 for (i = 0; s[0] != '\n' && i < cnt; i++)
995 s = uc_next(s);
996 if (i < cnt)
997 return 1;
998 pref = uc_sub(ln, 0, off);
999 post = uc_sub(ln, off + cnt, -1);
1000 sb = sbuf_make();
1001 sbuf_str(sb, pref);
1002 for (i = 0; i < cnt; i++)
1003 sbuf_str(sb, cs);
1004 sbuf_str(sb, post);
1005 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1006 off += cnt - 1;
1007 xoff = off;
1008 sbuf_free(sb);
1009 free(pref);
1010 free(post);
1011 return 0;
1014 static char rep_cmd[4096]; /* the last command */
1015 static int rep_len;
1017 static void vc_repeat(void)
1019 int i;
1020 for (i = 0; i < MAX(1, vi_arg1); i++)
1021 term_push(rep_cmd, rep_len);
1024 static void vc_execute(void)
1026 static int exec_buf;
1027 int lnmode;
1028 int c = vi_read();
1029 char *buf;
1030 int i;
1031 if (TK_INT(c))
1032 return;
1033 if (c == '@')
1034 c = exec_buf;
1035 exec_buf = c;
1036 buf = reg_get(exec_buf, &lnmode);
1037 if (buf)
1038 for (i = 0; i < MAX(1, vi_arg1); i++)
1039 term_push(buf, strlen(buf));
1042 static void vi(void)
1044 int xcol;
1045 int mark;
1046 char *ln;
1047 int kmap = 0;
1048 xtop = MAX(0, xrow - xrows / 2);
1049 xoff = 0;
1050 xcol = vi_off2col(xb, xrow, xoff);
1051 vi_drawagain(xcol, 0);
1052 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1053 while (!xquit) {
1054 int mod = 0; /* screen should be redrawn (1: the whole screen, 2: the current line) */
1055 int nrow = xrow;
1056 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1057 int otop = xtop;
1058 int oleft = xleft;
1059 int orow = xrow;
1060 int mv, n;
1061 term_cmd(&n);
1062 vi_arg2 = 0;
1063 vi_ybuf = vi_yankbuf();
1064 vi_arg1 = vi_prefix();
1065 if (!vi_ybuf)
1066 vi_ybuf = vi_yankbuf();
1067 mv = vi_motion(&nrow, &noff);
1068 if (mv > 0) {
1069 if (strchr("\'`GHML/?{}[]nN", mv) ||
1070 (mv == '%' && noff < 0)) {
1071 lbuf_mark(xb, '\'', xrow, xoff);
1072 lbuf_mark(xb, '`', xrow, xoff);
1074 xrow = nrow;
1075 if (noff < 0 && !strchr("jk", mv))
1076 noff = lbuf_indents(xb, xrow);
1077 if (strchr("jk", mv))
1078 noff = vi_col2off(xb, xrow, xcol);
1079 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1080 if (!strchr("|jk", mv))
1081 xcol = vi_off2col(xb, xrow, xoff);
1082 if (mv == '|')
1083 xcol = vi_pcol;
1084 } else if (mv == 0) {
1085 char *cmd;
1086 int c = vi_read();
1087 int k = 0;
1088 if (c <= 0)
1089 continue;
1090 lbuf_mark(xb, '*', xrow, xoff);
1091 switch (c) {
1092 case TK_CTL('b'):
1093 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1094 break;
1095 xoff = lbuf_indents(xb, xrow);
1096 mod = 1;
1097 break;
1098 case TK_CTL('f'):
1099 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1100 break;
1101 xoff = lbuf_indents(xb, xrow);
1102 mod = 1;
1103 break;
1104 case TK_CTL('e'):
1105 if (vi_scrollforeward(MAX(1, vi_arg1)))
1106 break;
1107 break;
1108 case TK_CTL('y'):
1109 if (vi_scrollbackward(MAX(1, vi_arg1)))
1110 break;
1111 break;
1112 case TK_CTL('u'):
1113 if (xrow == 0)
1114 break;
1115 if (vi_arg1)
1116 vi_scroll = vi_arg1;
1117 n = vi_scroll ? vi_scroll : xrows / 2;
1118 xrow = MAX(0, xrow - n);
1119 if (xtop > 0)
1120 xtop = MAX(0, xtop - n);
1121 xoff = lbuf_indents(xb, xrow);
1122 mod = 1;
1123 break;
1124 case TK_CTL('d'):
1125 if (xrow == lbuf_len(xb) - 1)
1126 break;
1127 if (vi_arg1)
1128 vi_scroll = vi_arg1;
1129 n = vi_scroll ? vi_scroll : xrows / 2;
1130 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1131 if (xtop < lbuf_len(xb) - xrows)
1132 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1133 xoff = lbuf_indents(xb, xrow);
1134 mod = 1;
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 xtd = k == 'r' ? -1 : +1;
1227 break;
1228 case 'L':
1229 case 'R':
1230 xtd = k == 'R' ? -2 : +2;
1231 break;
1232 case 'e':
1233 case 'f':
1234 xkmap = k == 'e' ? 0 : xkmap_alt;
1235 break;
1237 mod = 1;
1238 break;
1239 case 'g':
1240 k = vi_read();
1241 if (k == '~' || k == 'u' || k == 'U')
1242 if (!vc_motion(k))
1243 mod = 2;
1244 if (k == 'a')
1245 vc_charinfo();
1246 break;
1247 case 'x':
1248 vi_back(' ');
1249 if (!vc_motion('d'))
1250 mod = 2;
1251 break;
1252 case 'X':
1253 vi_back(TK_CTL('h'));
1254 if (!vc_motion('d'))
1255 mod = 2;
1256 break;
1257 case 'C':
1258 vi_back('$');
1259 if (!vc_motion('c'))
1260 mod = 1;
1261 break;
1262 case 'D':
1263 vi_back('$');
1264 if (!vc_motion('d'))
1265 mod = 2;
1266 break;
1267 case 'r':
1268 if (!vc_replace())
1269 mod = 2;
1270 break;
1271 case 's':
1272 vi_back(' ');
1273 if (!vc_motion('c'))
1274 mod = 1;
1275 break;
1276 case 'S':
1277 vi_back('c');
1278 if (!vc_motion('c'))
1279 mod = 1;
1280 break;
1281 case 'Y':
1282 vi_back('y');
1283 vc_motion('y');
1284 break;
1285 case 'Z':
1286 k = vi_read();
1287 if (k == 'Z')
1288 ex_command("x");
1289 break;
1290 case '~':
1291 vi_back(' ');
1292 if (!vc_motion('~'))
1293 mod = 2;
1294 break;
1295 case '.':
1296 vc_repeat();
1297 break;
1298 case '@':
1299 vc_execute();
1300 break;
1301 default:
1302 continue;
1304 cmd = term_cmd(&n);
1305 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1306 (c == 'g' && strchr("uU~", k))) {
1307 if (n < sizeof(rep_cmd)) {
1308 memcpy(rep_cmd, cmd, n);
1309 rep_len = n;
1313 if (xrow < 0 || xrow >= lbuf_len(xb))
1314 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1315 if (xtop > xrow)
1316 xtop = xtop - xrows / 2 > xrow ?
1317 MAX(0, xrow - xrows / 2) : xrow;
1318 if (xtop + xrows <= xrow)
1319 xtop = xtop + xrows + xrows / 2 <= xrow ?
1320 xrow - xrows / 2 : xrow - xrows + 1;
1321 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1322 if (mod)
1323 xcol = vi_off2col(xb, xrow, xoff);
1324 if (xcol >= xleft + xcols)
1325 xleft = xcol - xcols / 2;
1326 if (xcol < xleft)
1327 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1328 vi_wait();
1329 if (mod || xleft != oleft)
1330 vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
1331 else if (xtop != otop)
1332 vi_drawupdate(xcol, otop);
1333 if (vi_msg[0])
1334 vi_drawmsg();
1335 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1336 ren_cursor(lbuf_get(xb, xrow), xcol)));
1337 lbuf_modified(xb);
1339 term_pos(xrows, 0);
1340 term_kill();
1343 int main(int argc, char *argv[])
1345 int i;
1346 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1347 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1348 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1349 if (argv[i][1] == 's')
1350 xled = 0;
1351 if (argv[i][1] == 'e')
1352 xvis = 0;
1353 if (argv[i][1] == 'v')
1354 xvis = 1;
1356 dir_init();
1357 syn_init();
1358 if (xled || xvis)
1359 term_init();
1360 if (!ex_init(argv + i)) {
1361 if (xvis)
1362 vi();
1363 else
1364 ex();
1365 if (xled || xvis)
1366 term_done();
1367 ex_done();
1369 reg_done();
1370 syn_done();
1371 dir_done();
1372 return 0;