vi: explain when the screen is updated in vi()
[neatvi.git] / vi.c
blob96521a29213cdaf7288764c24f345c76a0f7aacf
1 /*
2 * NEATVI Editor
4 * Copyright (C) 2015-2017 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);
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(&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 char *s = led_prompt(msg, "", &xkmap);
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, &xkmap);
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 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
342 break;
343 case 'L':
344 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
345 break;
346 case 'M':
347 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
348 break;
349 default:
350 if (c == cmd) {
351 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
352 break;
354 if (c == '%' && (vi_arg1 || vi_arg2)) {
355 if (cnt > 100)
356 return -1;
357 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
358 break;
360 vi_back(c);
361 return 0;
363 if (*row < 0)
364 *row = 0;
365 return c;
368 static char *vi_curword(struct lbuf *lb, int row, int off)
370 struct sbuf *sb;
371 char *ln = lbuf_get(lb, row);
372 char *beg, *end;
373 if (!ln)
374 return NULL;
375 beg = uc_chr(ln, ren_noeol(ln, off));
376 end = beg;
377 while (*end && uc_kind(end) == 1)
378 end = uc_next(end);
379 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
380 beg = uc_beg(ln, beg - 1);
381 if (beg >= end)
382 return NULL;
383 sb = sbuf_make();
384 sbuf_str(sb, "\\<");
385 sbuf_mem(sb, beg, end - beg);
386 sbuf_str(sb, "\\>");
387 return sbuf_done(sb);
390 /* read a motion */
391 static int vi_motion(int *row, int *off)
393 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
394 char *ln = lbuf_get(xb, *row);
395 int dir = dir_context(ln ? ln : "");
396 int mark, mark_row, mark_off;
397 char *cs;
398 int mv;
399 int i;
400 if ((mv = vi_motionln(row, 0))) {
401 *off = -1;
402 return mv;
404 mv = vi_read();
405 switch (mv) {
406 case 'f':
407 if (!(cs = vi_char()))
408 return -1;
409 if (vi_findchar(xb, cs, mv, cnt, row, off))
410 return -1;
411 break;
412 case 'F':
413 if (!(cs = vi_char()))
414 return -1;
415 if (vi_findchar(xb, cs, mv, cnt, row, off))
416 return -1;
417 break;
418 case ';':
419 if (!vi_charlast[0])
420 return -1;
421 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
422 return -1;
423 break;
424 case ',':
425 if (!vi_charlast[0])
426 return -1;
427 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
428 return -1;
429 break;
430 case 'h':
431 for (i = 0; i < cnt; i++)
432 if (vi_nextcol(xb, -1 * dir, row, off))
433 break;
434 break;
435 case 'l':
436 for (i = 0; i < cnt; i++)
437 if (vi_nextcol(xb, +1 * dir, row, off))
438 break;
439 break;
440 case 't':
441 if (!(cs = vi_char()))
442 return -1;
443 if (vi_findchar(xb, cs, mv, cnt, row, off))
444 return -1;
445 break;
446 case 'T':
447 if (!(cs = vi_char()))
448 return -1;
449 if (vi_findchar(xb, cs, mv, cnt, row, off))
450 return -1;
451 break;
452 case 'B':
453 for (i = 0; i < cnt; i++)
454 if (lbuf_wordend(xb, 1, -1, row, off))
455 break;
456 break;
457 case 'E':
458 for (i = 0; i < cnt; i++)
459 if (lbuf_wordend(xb, 1, +1, row, off))
460 break;
461 break;
462 case 'W':
463 for (i = 0; i < cnt; i++)
464 if (lbuf_wordbeg(xb, 1, +1, row, off))
465 break;
466 break;
467 case 'b':
468 for (i = 0; i < cnt; i++)
469 if (lbuf_wordend(xb, 0, -1, row, off))
470 break;
471 break;
472 case 'e':
473 for (i = 0; i < cnt; i++)
474 if (lbuf_wordend(xb, 0, +1, row, off))
475 break;
476 break;
477 case 'w':
478 for (i = 0; i < cnt; i++)
479 if (lbuf_wordbeg(xb, 0, +1, row, off))
480 break;
481 break;
482 case '{':
483 for (i = 0; i < cnt; i++)
484 if (lbuf_paragraphbeg(xb, -1, row, off))
485 break;
486 break;
487 case '}':
488 for (i = 0; i < cnt; i++)
489 if (lbuf_paragraphbeg(xb, +1, row, off))
490 break;
491 break;
492 case '[':
493 if (vi_read() != '[')
494 return -1;
495 for (i = 0; i < cnt; i++)
496 if (lbuf_sectionbeg(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 '0':
507 *off = 0;
508 break;
509 case '^':
510 *off = lbuf_indents(xb, *row);
511 break;
512 case '$':
513 *off = lbuf_eol(xb, *row);
514 break;
515 case '|':
516 *off = vi_col2off(xb, *row, cnt - 1);
517 vi_pcol = cnt - 1;
518 break;
519 case '/':
520 if (vi_search(mv, cnt, row, off))
521 return -1;
522 break;
523 case '?':
524 if (vi_search(mv, cnt, row, off))
525 return -1;
526 break;
527 case 'n':
528 if (vi_search(mv, cnt, row, off))
529 return -1;
530 break;
531 case 'N':
532 if (vi_search(mv, cnt, row, off))
533 return -1;
534 break;
535 case TK_CTL('a'):
536 if (!(cs = vi_curword(xb, *row, *off)))
537 return -1;
538 ex_kwdset(cs, +1);
539 free(cs);
540 if (vi_search('n', cnt, row, off))
541 return -1;
542 break;
543 case ' ':
544 for (i = 0; i < cnt; i++)
545 if (vi_nextoff(xb, +1, row, off))
546 break;
547 break;
548 case 127:
549 case TK_CTL('h'):
550 for (i = 0; i < cnt; i++)
551 if (vi_nextoff(xb, -1, row, off))
552 break;
553 break;
554 case '`':
555 if ((mark = vi_read()) <= 0)
556 return -1;
557 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
558 return -1;
559 *row = mark_row;
560 *off = mark_off;
561 break;
562 case '%':
563 if (lbuf_pair(xb, row, off))
564 return -1;
565 break;
566 default:
567 vi_back(mv);
568 return 0;
570 return mv;
573 static void swap(int *a, int *b)
575 int t = *a;
576 *a = *b;
577 *b = t;
580 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
582 struct sbuf *sb;
583 char *s1, *s2, *s3;
584 if (r1 == r2)
585 return uc_sub(lbuf_get(lb, r1), o1, o2);
586 sb = sbuf_make();
587 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
588 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
589 s2 = lbuf_cp(lb, r1 + 1, r2);
590 sbuf_str(sb, s1);
591 sbuf_str(sb, s2);
592 sbuf_str(sb, s3);
593 free(s1);
594 free(s2);
595 free(s3);
596 return sbuf_done(sb);
599 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
601 char *region;
602 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
603 reg_put(vi_ybuf, region, lnmode);
604 free(region);
605 xrow = r1;
606 xoff = lnmode ? xoff : o1;
609 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
611 char *pref, *post;
612 char *region;
613 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
614 reg_put(vi_ybuf, region, lnmode);
615 free(region);
616 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
617 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
618 if (!lnmode) {
619 struct sbuf *sb = sbuf_make();
620 sbuf_str(sb, pref);
621 sbuf_str(sb, post);
622 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
623 sbuf_free(sb);
624 } else {
625 lbuf_edit(xb, NULL, r1, r2 + 1);
627 xrow = r1;
628 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
629 free(pref);
630 free(post);
633 static int linecount(char *s)
635 int n;
636 for (n = 0; s; n++)
637 if ((s = strchr(s, '\n')))
638 s++;
639 return n;
642 static int charcount(char *text, char *post)
644 int tlen = strlen(text);
645 int plen = strlen(post);
646 char *nl = text;
647 int i;
648 if (tlen < plen)
649 return 0;
650 for (i = 0; i < tlen - plen; i++)
651 if (text[i] == '\n')
652 nl = text + i + 1;
653 return uc_slen(nl) - uc_slen(post);
656 static char *vi_input(char *pref, char *post, int *row, int *off)
658 char *rep = led_input(pref, post, &xkmap);
659 if (!rep)
660 return NULL;
661 *row = linecount(rep) - 1;
662 *off = charcount(rep, post) - 1;
663 if (*off < 0)
664 *off = 0;
665 return rep;
668 static char *vi_indents(char *ln)
670 struct sbuf *sb = sbuf_make();
671 while (xai && ln && (*ln == ' ' || *ln == '\t'))
672 sbuf_chr(sb, *ln++);
673 return sbuf_done(sb);
676 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
678 char *region;
679 int row, off;
680 char *rep;
681 char *pref, *post;
682 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
683 reg_put(vi_ybuf, region, lnmode);
684 free(region);
685 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
686 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
687 vi_drawrm(r1, r2, 0);
688 rep = vi_input(pref, post, &row, &off);
689 if (rep) {
690 lbuf_edit(xb, rep, r1, r2 + 1);
691 xrow = r1 + row - 1;
692 xoff = off;
693 free(rep);
695 free(pref);
696 free(post);
699 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
701 char *pref, *post;
702 char *region, *s;
703 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
704 s = region;
705 while (*s) {
706 int c = (unsigned char) s[0];
707 if (c <= 0x7f) {
708 if (cmd == 'u')
709 s[0] = tolower(c);
710 if (cmd == 'U')
711 s[0] = toupper(c);
712 if (cmd == '~')
713 s[0] = islower(c) ? toupper(c) : tolower(c);
715 s = uc_next(s);
717 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
718 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
719 if (!lnmode) {
720 struct sbuf *sb = sbuf_make();
721 sbuf_str(sb, pref);
722 sbuf_str(sb, region);
723 sbuf_str(sb, post);
724 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
725 sbuf_free(sb);
726 } else {
727 lbuf_edit(xb, region, r1, r2 + 1);
729 xrow = r2;
730 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
731 free(region);
732 free(pref);
733 free(post);
736 static void vi_pipe(int r1, int r2)
738 char *text;
739 char *rep;
740 int kmap = 0;
741 char *cmd = vi_prompt("!", &kmap);
742 if (!cmd)
743 return;
744 text = lbuf_cp(xb, r1, r2 + 1);
745 rep = cmd_pipe(cmd, text, 1, 1);
746 if (rep)
747 lbuf_edit(xb, rep, r1, r2 + 1);
748 free(cmd);
749 free(text);
750 free(rep);
753 static void vi_shift(int r1, int r2, int dir)
755 struct sbuf *sb;
756 char *ln;
757 int i;
758 for (i = r1; i <= r2; i++) {
759 if (!(ln = lbuf_get(xb, i)))
760 continue;
761 sb = sbuf_make();
762 if (dir > 0)
763 sbuf_chr(sb, '\t');
764 else
765 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
766 sbuf_str(sb, ln);
767 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
768 sbuf_free(sb);
770 xrow = r1;
771 xoff = lbuf_indents(xb, xrow);
774 static int vc_motion(int cmd)
776 int r1 = xrow, r2 = xrow; /* region rows */
777 int o1 = xoff, o2 = xoff; /* visual region columns */
778 int lnmode = 0; /* line-based region */
779 int mv;
780 vi_arg2 = vi_prefix();
781 if (vi_arg2 < 0)
782 return 1;
783 o1 = ren_noeol(lbuf_get(xb, r1), o1);
784 o2 = o1;
785 if ((mv = vi_motionln(&r2, cmd))) {
786 o2 = -1;
787 } else if (!(mv = vi_motion(&r2, &o2))) {
788 vi_read();
789 return 1;
791 if (mv < 0)
792 return 1;
793 lnmode = o2 < 0;
794 if (lnmode) {
795 o1 = 0;
796 o2 = lbuf_eol(xb, r2);
798 if (r1 > r2) {
799 swap(&r1, &r2);
800 swap(&o1, &o2);
802 if (r1 == r2 && o1 > o2)
803 swap(&o1, &o2);
804 o1 = ren_noeol(lbuf_get(xb, r1), o1);
805 if (!lnmode && strchr("fFtTeE%", mv))
806 if (o2 < lbuf_eol(xb, r2))
807 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
808 if (cmd == 'y')
809 vi_yank(r1, o1, r2, o2, lnmode);
810 if (cmd == 'd')
811 vi_delete(r1, o1, r2, o2, lnmode);
812 if (cmd == 'c')
813 vi_change(r1, o1, r2, o2, lnmode);
814 if (cmd == '~' || cmd == 'u' || cmd == 'U')
815 vi_case(r1, o1, r2, o2, lnmode, cmd);
816 if (cmd == '!')
817 vi_pipe(r1, r2);
818 if (cmd == '>' || cmd == '<')
819 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
820 return 0;
823 static int vc_insert(int cmd)
825 char *pref, *post;
826 char *ln = lbuf_get(xb, xrow);
827 int row, off = 0;
828 char *rep;
829 if (cmd == 'I')
830 xoff = lbuf_indents(xb, xrow);
831 if (cmd == 'A')
832 xoff = lbuf_eol(xb, xrow);
833 xoff = ren_noeol(ln, xoff);
834 if (cmd == 'o')
835 xrow += 1;
836 if (cmd == 'i' || cmd == 'I')
837 off = xoff;
838 if (cmd == 'a' || cmd == 'A')
839 off = xoff + 1;
840 if (ln && ln[0] == '\n')
841 off = 0;
842 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
843 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
844 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
845 rep = vi_input(pref, post, &row, &off);
846 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
847 lbuf_edit(xb, "\n", 0, 0);
848 if (rep) {
849 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
850 xrow += row - 1;
851 xoff = off;
852 free(rep);
854 free(pref);
855 free(post);
856 return !rep;
859 static int vc_put(int cmd)
861 int cnt = MAX(1, vi_arg1);
862 int lnmode;
863 char *buf = reg_get(vi_ybuf, &lnmode);
864 int i;
865 if (!buf) {
866 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
867 return 1;
869 if (lnmode) {
870 struct sbuf *sb = sbuf_make();
871 for (i = 0; i < cnt; i++)
872 sbuf_str(sb, buf);
873 if (!lbuf_len(xb))
874 lbuf_edit(xb, "\n", 0, 0);
875 if (cmd == 'p')
876 xrow++;
877 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
878 xoff = lbuf_indents(xb, xrow);
879 sbuf_free(sb);
880 } else {
881 struct sbuf *sb = sbuf_make();
882 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
883 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
884 char *s = uc_sub(ln, 0, off);
885 sbuf_str(sb, s);
886 free(s);
887 for (i = 0; i < cnt; i++)
888 sbuf_str(sb, buf);
889 s = uc_sub(ln, off, -1);
890 sbuf_str(sb, s);
891 free(s);
892 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
893 xoff = off + uc_slen(buf) * cnt - 1;
894 sbuf_free(sb);
896 return 0;
899 static int join_spaces(char *prev, char *next)
901 int prevlen = strlen(prev);
902 if (!prev[0])
903 return 0;
904 if (prev[prevlen - 1] == ' ' || next[0] == ')')
905 return 0;
906 return prev[prevlen - 1] == '.' ? 2 : 1;
909 static int vc_join(void)
911 struct sbuf *sb;
912 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
913 int beg = xrow;
914 int end = xrow + cnt;
915 int off = 0;
916 int i;
917 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
918 return 1;
919 sb = sbuf_make();
920 for (i = beg; i < end; i++) {
921 char *ln = lbuf_get(xb, i);
922 char *lnend = strchr(ln, '\n');
923 int spaces;
924 if (i > beg)
925 while (ln[0] == ' ' || ln[0] == '\t')
926 ln++;
927 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
928 off = uc_slen(sbuf_buf(sb));
929 while (spaces--)
930 sbuf_chr(sb, ' ');
931 sbuf_mem(sb, ln, lnend - ln);
933 sbuf_chr(sb, '\n');
934 lbuf_edit(xb, sbuf_buf(sb), beg, end);
935 xoff = off;
936 sbuf_free(sb);
937 return 0;
940 static int vi_scrollforeward(int cnt)
942 if (xtop >= lbuf_len(xb) - 1)
943 return 1;
944 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
945 xrow = MAX(xrow, xtop);
946 return 0;
949 static int vi_scrollbackward(int cnt)
951 if (xtop == 0)
952 return 1;
953 xtop = MAX(0, xtop - cnt);
954 xrow = MIN(xrow, xtop + xrows - 1);
955 return 0;
958 static void vc_status(void)
960 int col = vi_off2col(xb, xrow, xoff);
961 snprintf(vi_msg, sizeof(vi_msg),
962 "\"%s\"%c %d lines L%d C%d\n",
963 ex_path()[0] ? ex_path() : "unnamed",
964 lbuf_modified(xb) ? '*' : ' ',
965 lbuf_len(xb), xrow + 1,
966 ren_cursor(lbuf_get(xb, xrow), col) + 1);
969 static void vc_charinfo(void)
971 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
972 if (c) {
973 char cbuf[8] = "";
974 memcpy(cbuf, c, uc_len(c));
975 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
979 static int vc_replace(void)
981 int cnt = MAX(1, vi_arg1);
982 char *cs = vi_char();
983 char *ln = lbuf_get(xb, xrow);
984 struct sbuf *sb;
985 char *pref, *post;
986 char *s;
987 int off, i;
988 if (!ln || !cs)
989 return 1;
990 off = ren_noeol(ln, xoff);
991 s = uc_chr(ln, off);
992 for (i = 0; s[0] != '\n' && i < cnt; i++)
993 s = uc_next(s);
994 if (i < cnt)
995 return 1;
996 pref = uc_sub(ln, 0, off);
997 post = uc_sub(ln, off + cnt, -1);
998 sb = sbuf_make();
999 sbuf_str(sb, pref);
1000 for (i = 0; i < cnt; i++)
1001 sbuf_str(sb, cs);
1002 sbuf_str(sb, post);
1003 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1004 off += cnt - 1;
1005 xoff = off;
1006 sbuf_free(sb);
1007 free(pref);
1008 free(post);
1009 return 0;
1012 static char rep_cmd[4096]; /* the last command */
1013 static int rep_len;
1015 static void vc_repeat(void)
1017 int i;
1018 for (i = 0; i < MAX(1, vi_arg1); i++)
1019 term_push(rep_cmd, rep_len);
1022 static void vc_execute(void)
1024 static int exec_buf;
1025 int lnmode;
1026 int c = vi_read();
1027 char *buf;
1028 int i;
1029 if (TK_INT(c))
1030 return;
1031 if (c == '@')
1032 c = exec_buf;
1033 exec_buf = c;
1034 buf = reg_get(exec_buf, &lnmode);
1035 if (buf)
1036 for (i = 0; i < MAX(1, vi_arg1); i++)
1037 term_push(buf, strlen(buf));
1040 static void vi(void)
1042 int xcol;
1043 int mark;
1044 char *ln;
1045 int kmap = 0;
1046 xtop = MAX(0, xrow - xrows / 2);
1047 xoff = 0;
1048 xcol = vi_off2col(xb, xrow, xoff);
1049 vi_drawagain(xcol, 0);
1050 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1051 while (!xquit) {
1052 int mod = 0; /* screen should be redrawn (1: current line, 2: the whole screen */
1053 int nrow = xrow;
1054 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1055 int otop = xtop;
1056 int oleft = xleft;
1057 int mv, n;
1058 term_cmd(&n);
1059 vi_arg2 = 0;
1060 vi_ybuf = vi_yankbuf();
1061 vi_arg1 = vi_prefix();
1062 if (!vi_ybuf)
1063 vi_ybuf = vi_yankbuf();
1064 mv = vi_motion(&nrow, &noff);
1065 if (mv > 0) {
1066 if (strchr("\'`GHML/?{}[]nN", mv) ||
1067 (mv == '%' && noff < 0)) {
1068 lbuf_mark(xb, '\'', xrow, xoff);
1069 lbuf_mark(xb, '`', xrow, xoff);
1071 xrow = nrow;
1072 if (noff < 0 && !strchr("jk", mv))
1073 noff = lbuf_indents(xb, xrow);
1074 if (strchr("jk", mv))
1075 noff = vi_col2off(xb, xrow, xcol);
1076 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1077 if (!strchr("|jk", mv))
1078 xcol = vi_off2col(xb, xrow, xoff);
1079 if (mv == '|')
1080 xcol = vi_pcol;
1081 } else if (mv == 0) {
1082 char *cmd;
1083 int c = vi_read();
1084 int k = 0;
1085 if (c <= 0)
1086 continue;
1087 lbuf_mark(xb, '*', xrow, xoff);
1088 switch (c) {
1089 case TK_CTL('b'):
1090 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1091 break;
1092 xoff = lbuf_indents(xb, xrow);
1093 mod = 1;
1094 break;
1095 case TK_CTL('f'):
1096 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1097 break;
1098 xoff = lbuf_indents(xb, xrow);
1099 mod = 1;
1100 break;
1101 case TK_CTL('e'):
1102 if (vi_scrollforeward(MAX(1, vi_arg1)))
1103 break;
1104 break;
1105 case TK_CTL('y'):
1106 if (vi_scrollbackward(MAX(1, vi_arg1)))
1107 break;
1108 break;
1109 case TK_CTL('u'):
1110 if (xrow == 0)
1111 break;
1112 if (vi_arg1)
1113 vi_scroll = vi_arg1;
1114 n = vi_scroll ? vi_scroll : xrows / 2;
1115 xrow = MAX(0, xrow - n);
1116 if (xtop > 0)
1117 xtop = MAX(0, xtop - n);
1118 xoff = lbuf_indents(xb, xrow);
1119 mod = 1;
1120 break;
1121 case TK_CTL('d'):
1122 if (xrow == lbuf_len(xb) - 1)
1123 break;
1124 if (vi_arg1)
1125 vi_scroll = vi_arg1;
1126 n = vi_scroll ? vi_scroll : xrows / 2;
1127 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1128 if (xtop < lbuf_len(xb) - xrows)
1129 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1130 xoff = lbuf_indents(xb, xrow);
1131 mod = 1;
1132 break;
1133 case TK_CTL('z'):
1134 term_pos(xrows, 0);
1135 term_suspend();
1136 mod = 1;
1137 break;
1138 case 'u':
1139 if (!lbuf_undo(xb)) {
1140 lbuf_jump(xb, '*', &xrow, &xoff);
1141 mod = 1;
1142 } else {
1143 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1145 break;
1146 case TK_CTL('r'):
1147 if (!lbuf_redo(xb)) {
1148 lbuf_jump(xb, '*', &xrow, &xoff);
1149 mod = 1;
1150 } else {
1151 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1153 break;
1154 case TK_CTL('g'):
1155 vc_status();
1156 break;
1157 case TK_CTL('^'):
1158 ex_command("e #");
1159 mod = 1;
1160 break;
1161 case ':':
1162 ln = vi_prompt(":", &kmap);
1163 if (ln && ln[0]) {
1164 ex_command(ln);
1165 mod = 1;
1167 free(ln);
1168 if (xquit)
1169 continue;
1170 break;
1171 case 'c':
1172 case 'd':
1173 case 'y':
1174 case '!':
1175 case '>':
1176 case '<':
1177 if (!vc_motion(c))
1178 mod = 1;
1179 break;
1180 case 'i':
1181 case 'I':
1182 case 'a':
1183 case 'A':
1184 case 'o':
1185 case 'O':
1186 if (!vc_insert(c))
1187 mod = 1;
1188 break;
1189 case 'J':
1190 if (!vc_join())
1191 mod = 1;
1192 break;
1193 case TK_CTL('l'):
1194 term_done();
1195 term_init();
1196 mod = 1;
1197 break;
1198 case 'm':
1199 if ((mark = vi_read()) > 0 && islower(mark))
1200 lbuf_mark(xb, mark, xrow, xoff);
1201 break;
1202 case 'p':
1203 case 'P':
1204 if (!vc_put(c))
1205 mod = 1;
1206 break;
1207 case 'z':
1208 k = vi_read();
1209 switch (k) {
1210 case '\n':
1211 xtop = vi_arg1 ? vi_arg1 : xrow;
1212 break;
1213 case '.':
1214 n = vi_arg1 ? vi_arg1 : xrow;
1215 xtop = MAX(0, n - xrows / 2);
1216 break;
1217 case '-':
1218 n = vi_arg1 ? vi_arg1 : xrow;
1219 xtop = MAX(0, n - xrows + 1);
1220 break;
1221 case 'l':
1222 case 'r':
1223 xtd = k == 'r' ? -1 : +1;
1224 break;
1225 case 'L':
1226 case 'R':
1227 xtd = k == 'R' ? -2 : +2;
1228 break;
1229 case 'e':
1230 case 'f':
1231 xkmap = k == 'e' ? 0 : xkmap_alt;
1232 break;
1234 mod = 1;
1235 break;
1236 case 'g':
1237 k = vi_read();
1238 if (k == '~' || k == 'u' || k == 'U')
1239 if (!vc_motion(k))
1240 mod = 1;
1241 if (k == 'a')
1242 vc_charinfo();
1243 break;
1244 case 'x':
1245 vi_back(' ');
1246 if (!vc_motion('d'))
1247 mod = 2;
1248 break;
1249 case 'X':
1250 vi_back(TK_CTL('h'));
1251 if (!vc_motion('d'))
1252 mod = 2;
1253 break;
1254 case 'C':
1255 vi_back('$');
1256 if (!vc_motion('c'))
1257 mod = 1;
1258 break;
1259 case 'D':
1260 vi_back('$');
1261 if (!vc_motion('d'))
1262 mod = 2;
1263 break;
1264 case 'r':
1265 if (!vc_replace())
1266 mod = 2;
1267 break;
1268 case 's':
1269 vi_back(' ');
1270 if (!vc_motion('c'))
1271 mod = 1;
1272 break;
1273 case 'S':
1274 vi_back('c');
1275 if (!vc_motion('c'))
1276 mod = 1;
1277 break;
1278 case 'Y':
1279 vi_back('y');
1280 vc_motion('y');
1281 break;
1282 case 'Z':
1283 k = vi_read();
1284 if (k == 'Z')
1285 ex_command("x");
1286 break;
1287 case '~':
1288 vi_back(' ');
1289 if (!vc_motion('~'))
1290 mod = 2;
1291 break;
1292 case '.':
1293 vc_repeat();
1294 break;
1295 case '@':
1296 vc_execute();
1297 break;
1298 default:
1299 continue;
1301 cmd = term_cmd(&n);
1302 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1303 c == 'g' && strchr("uU~", k)) {
1304 if (n < sizeof(rep_cmd)) {
1305 memcpy(rep_cmd, cmd, n);
1306 rep_len = n;
1310 if (xrow < 0 || xrow >= lbuf_len(xb))
1311 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1312 if (xtop > xrow)
1313 xtop = xtop - xrows / 2 > xrow ?
1314 MAX(0, xrow - xrows / 2) : xrow;
1315 if (xtop + xrows <= xrow)
1316 xtop = xtop + xrows + xrows / 2 <= xrow ?
1317 xrow - xrows / 2 : xrow - xrows + 1;
1318 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1319 if (mod)
1320 xcol = vi_off2col(xb, xrow, xoff);
1321 if (xcol >= xleft + xcols)
1322 xleft = xcol - xcols / 2;
1323 if (xcol < xleft)
1324 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1325 vi_wait();
1326 if (mod || xleft != oleft)
1327 vi_drawagain(xcol, mod == 2 && xleft == oleft);
1328 else if (xtop != otop)
1329 vi_drawupdate(xcol, otop);
1330 if (vi_msg[0])
1331 vi_drawmsg();
1332 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1333 ren_cursor(lbuf_get(xb, xrow), xcol)));
1334 lbuf_modified(xb);
1336 term_pos(xrows, 0);
1337 term_kill();
1340 int main(int argc, char *argv[])
1342 int i;
1343 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1344 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1345 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1346 if (argv[i][1] == 's')
1347 xled = 0;
1348 if (argv[i][1] == 'e')
1349 xvis = 0;
1350 if (argv[i][1] == 'v')
1351 xvis = 1;
1353 dir_init();
1354 syn_init();
1355 if (xled || xvis)
1356 term_init();
1357 if (!ex_init(argv + i)) {
1358 if (xvis)
1359 vi();
1360 else
1361 ex();
1362 if (xled || xvis)
1363 term_done();
1364 ex_done();
1366 reg_done();
1367 syn_done();
1368 dir_done();
1369 return 0;