vi: call term_done() even if ex_init() fails
[neatvi.git] / vi.c
blobc9d56a190db33c64d34f605b28d50f208c83ab8d
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 if (xhll && row == xrow)
57 syn_context(conf_hlline());
58 led_print(s ? s : (row ? "~" : ""), row - xtop, ex_filetype());
59 syn_context(0);
62 /* redraw the screen */
63 static void vi_drawagain(int xcol, int lineonly)
65 int i;
66 term_record();
67 for (i = xtop; i < xtop + xrows; i++)
68 if (!lineonly || i == xrow)
69 vi_drawrow(i);
70 vi_drawmsg();
71 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
72 term_commit();
75 /* update the screen */
76 static void vi_drawupdate(int xcol, int otop)
78 int i = 0;
79 if (otop != xtop) {
80 term_record();
81 term_pos(0, 0);
82 term_room(otop - xtop);
83 if (xtop > otop) {
84 int n = MIN(xtop - otop, xrows);
85 for (i = 0; i < n; i++)
86 vi_drawrow(xtop + xrows - n + i);
87 } else {
88 int n = MIN(otop - xtop, xrows);
89 for (i = 0; i < n; i++)
90 vi_drawrow(xtop + i);
92 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
93 term_commit();
95 vi_drawmsg();
96 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
99 /* update the screen by removing lines r1 to r2 before an input command */
100 static void vi_drawrm(int r1, int r2, int newln)
102 r1 = MIN(MAX(r1, xtop), xtop + xrows);
103 r2 = MIN(MAX(r2, xtop), xtop + xrows);
104 term_pos(r1 - xtop, 0);
105 term_room(r1 - r2 + newln);
108 static int vi_buf[128];
109 static int vi_buflen;
111 static int vi_read(void)
113 return vi_buflen ? vi_buf[--vi_buflen] : term_read();
116 static void vi_back(int c)
118 if (vi_buflen < sizeof(vi_buf))
119 vi_buf[vi_buflen++] = c;
122 static char *vi_char(void)
124 return led_read(&xkmap);
127 static char *vi_prompt(char *msg, int *kmap)
129 char *r, *s;
130 term_pos(xrows, led_pos(msg, 0));
131 term_kill();
132 s = led_prompt(msg, "", kmap, "---");
133 if (!s)
134 return NULL;
135 r = uc_dup(strlen(s) >= strlen(msg) ? s + strlen(msg) : s);
136 free(s);
137 return r;
140 /* read an ex input line */
141 char *ex_read(char *msg)
143 struct sbuf *sb;
144 char c;
145 if (xled) {
146 int oleft = xleft;
147 char *s = led_prompt(msg, "", &xkmap, "---");
148 xleft = oleft;
149 if (s)
150 term_chr('\n');
151 return s;
153 sb = sbuf_make();
154 while ((c = getchar()) != EOF && c != '\n')
155 sbuf_chr(sb, c);
156 if (c == EOF) {
157 sbuf_free(sb);
158 return NULL;
160 return sbuf_done(sb);
163 /* show an ex message */
164 void ex_show(char *msg)
166 if (xvis) {
167 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
168 } else if (xled) {
169 led_print(msg, -1, "---");
170 term_chr('\n');
171 } else {
172 printf("%s", msg);
176 /* print an ex output line */
177 void ex_print(char *line)
179 if (xvis) {
180 vi_printed += line ? 1 : 2;
181 if (line)
182 snprintf(vi_msg, sizeof(vi_msg), "%s", line);
183 if (line)
184 led_print(line, -1, "");
185 term_chr('\n');
186 } else {
187 if (line)
188 ex_show(line);
192 static int vi_yankbuf(void)
194 int c = vi_read();
195 if (c == '"')
196 return vi_read();
197 vi_back(c);
198 return 0;
201 static int vi_prefix(void)
203 int n = 0;
204 int c = vi_read();
205 if ((c >= '1' && c <= '9')) {
206 while (isdigit(c)) {
207 n = n * 10 + c - '0';
208 c = vi_read();
211 vi_back(c);
212 return n;
215 static int vi_col2off(struct lbuf *lb, int row, int col)
217 char *ln = lbuf_get(lb, row);
218 return ln ? ren_off(ln, col) : 0;
221 static int vi_off2col(struct lbuf *lb, int row, int off)
223 char *ln = lbuf_get(lb, row);
224 return ln ? ren_pos(ln, off) : 0;
227 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
229 int o = *off + dir;
230 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
231 return 1;
232 *off = o;
233 return 0;
236 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
238 char *ln = lbuf_get(lb, *row);
239 int col = ln ? ren_pos(ln, *off) : 0;
240 int o = ln ? ren_next(ln, col, dir) : -1;
241 if (o < 0)
242 return -1;
243 *off = ren_off(ln, o);
244 return 0;
247 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
249 strcpy(vi_charlast, cs);
250 vi_charcmd = cmd;
251 return lbuf_findchar(lb, cs, cmd, n, row, off);
254 static int vi_search(int cmd, int cnt, int *row, int *off)
256 char *kwd;
257 int r = *row;
258 int o = *off;
259 int failed = 0;
260 int len = 0;
261 int i, dir;
262 if (cmd == '/' || cmd == '?') {
263 char sign[4] = {cmd};
264 struct sbuf *sb;
265 char *kw = vi_prompt(sign, &xkmap);
266 char *re;
267 if (!kw)
268 return 1;
269 sb = sbuf_make();
270 sbuf_chr(sb, cmd);
271 sbuf_str(sb, kw);
272 free(kw);
273 kw = sbuf_buf(sb);
274 if ((re = re_read(&kw))) {
275 ex_kwdset(re[0] ? re : NULL, cmd == '/' ? +1 : -1);
276 while (isspace(*kw))
277 kw++;
278 vi_soset = !!kw[0];
279 vi_so = atoi(kw);
280 free(re);
282 sbuf_free(sb);
284 if (!lbuf_len(xb) || ex_kwd(&kwd, &dir))
285 return 1;
286 dir = cmd == 'N' ? -dir : dir;
287 o = *off;
288 for (i = 0; i < cnt; i++) {
289 if (lbuf_search(xb, kwd, dir, &r, &o, &len)) {
290 failed = 1;
291 break;
293 if (i + 1 < cnt && cmd == '/')
294 o += len;
296 if (!failed) {
297 *row = r;
298 *off = o;
299 if (vi_soset) {
300 *off = -1;
301 if (*row + vi_so < 0 || *row + vi_so >= lbuf_len(xb))
302 failed = 1;
303 else
304 *row += vi_so;
307 if (failed)
308 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", kwd);
309 return failed;
312 /* read a line motion */
313 static int vi_motionln(int *row, int cmd)
315 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
316 int c = vi_read();
317 int mark, mark_row, mark_off;
318 switch (c) {
319 case '\n':
320 case '+':
321 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
322 break;
323 case '-':
324 *row = MAX(*row - cnt, 0);
325 break;
326 case '_':
327 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
328 break;
329 case '\'':
330 if ((mark = vi_read()) <= 0)
331 return -1;
332 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
333 return -1;
334 *row = mark_row;
335 break;
336 case 'j':
337 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
338 break;
339 case 'k':
340 *row = MAX(*row - cnt, 0);
341 break;
342 case 'G':
343 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
344 break;
345 case 'H':
346 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
347 break;
348 case 'L':
349 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
350 break;
351 case 'M':
352 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
353 break;
354 default:
355 if (c == cmd) {
356 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
357 break;
359 if (c == '%' && (vi_arg1 || vi_arg2)) {
360 if (cnt > 100)
361 return -1;
362 *row = MAX(0, lbuf_len(xb) - 1) * cnt / 100;
363 break;
365 vi_back(c);
366 return 0;
368 if (*row < 0)
369 *row = 0;
370 return c;
373 static char *vi_curword(struct lbuf *lb, int row, int off)
375 struct sbuf *sb;
376 char *ln = lbuf_get(lb, row);
377 char *beg, *end;
378 if (!ln)
379 return NULL;
380 beg = uc_chr(ln, ren_noeol(ln, off));
381 end = beg;
382 while (*end && uc_kind(end) == 1)
383 end = uc_next(end);
384 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
385 beg = uc_beg(ln, beg - 1);
386 if (beg >= end)
387 return NULL;
388 sb = sbuf_make();
389 sbuf_str(sb, "\\<");
390 sbuf_mem(sb, beg, end - beg);
391 sbuf_str(sb, "\\>");
392 return sbuf_done(sb);
395 /* read a motion */
396 static int vi_motion(int *row, int *off)
398 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
399 char *ln = lbuf_get(xb, *row);
400 int dir = dir_context(ln ? ln : "");
401 int mark, mark_row, mark_off;
402 char *cs;
403 int mv;
404 int i;
405 if ((mv = vi_motionln(row, 0))) {
406 *off = -1;
407 return mv;
409 mv = vi_read();
410 switch (mv) {
411 case 'f':
412 if (!(cs = vi_char()))
413 return -1;
414 if (vi_findchar(xb, cs, mv, cnt, row, off))
415 return -1;
416 break;
417 case 'F':
418 if (!(cs = vi_char()))
419 return -1;
420 if (vi_findchar(xb, cs, mv, cnt, row, off))
421 return -1;
422 break;
423 case ';':
424 if (!vi_charlast[0])
425 return -1;
426 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
427 return -1;
428 break;
429 case ',':
430 if (!vi_charlast[0])
431 return -1;
432 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
433 return -1;
434 break;
435 case 'h':
436 for (i = 0; i < cnt; i++)
437 if (vi_nextcol(xb, -1 * dir, row, off))
438 break;
439 break;
440 case 'l':
441 for (i = 0; i < cnt; i++)
442 if (vi_nextcol(xb, +1 * dir, row, off))
443 break;
444 break;
445 case 't':
446 if (!(cs = vi_char()))
447 return -1;
448 if (vi_findchar(xb, cs, mv, cnt, row, off))
449 return -1;
450 break;
451 case 'T':
452 if (!(cs = vi_char()))
453 return -1;
454 if (vi_findchar(xb, cs, mv, cnt, row, off))
455 return -1;
456 break;
457 case 'B':
458 for (i = 0; i < cnt; i++)
459 if (lbuf_wordend(xb, 1, -1, row, off))
460 break;
461 break;
462 case 'E':
463 for (i = 0; i < cnt; i++)
464 if (lbuf_wordend(xb, 1, +1, row, off))
465 break;
466 break;
467 case 'W':
468 for (i = 0; i < cnt; i++)
469 if (lbuf_wordbeg(xb, 1, +1, row, off))
470 break;
471 break;
472 case 'b':
473 for (i = 0; i < cnt; i++)
474 if (lbuf_wordend(xb, 0, -1, row, off))
475 break;
476 break;
477 case 'e':
478 for (i = 0; i < cnt; i++)
479 if (lbuf_wordend(xb, 0, +1, row, off))
480 break;
481 break;
482 case 'w':
483 for (i = 0; i < cnt; i++)
484 if (lbuf_wordbeg(xb, 0, +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 for (i = 0; i < cnt; i++)
494 if (lbuf_paragraphbeg(xb, +1, row, off))
495 break;
496 break;
497 case '[':
498 if (vi_read() != '[')
499 return -1;
500 for (i = 0; i < cnt; i++)
501 if (lbuf_sectionbeg(xb, -1, row, off))
502 break;
503 break;
504 case ']':
505 if (vi_read() != ']')
506 return -1;
507 for (i = 0; i < cnt; i++)
508 if (lbuf_sectionbeg(xb, +1, row, off))
509 break;
510 break;
511 case '0':
512 *off = 0;
513 break;
514 case '^':
515 *off = lbuf_indents(xb, *row);
516 break;
517 case '$':
518 *off = lbuf_eol(xb, *row);
519 break;
520 case '|':
521 *off = vi_col2off(xb, *row, cnt - 1);
522 vi_pcol = cnt - 1;
523 break;
524 case '/':
525 if (vi_search(mv, cnt, row, off))
526 return -1;
527 break;
528 case '?':
529 if (vi_search(mv, cnt, row, off))
530 return -1;
531 break;
532 case 'n':
533 if (vi_search(mv, cnt, row, off))
534 return -1;
535 break;
536 case 'N':
537 if (vi_search(mv, cnt, row, off))
538 return -1;
539 break;
540 case TK_CTL('a'):
541 if (!(cs = vi_curword(xb, *row, *off)))
542 return -1;
543 ex_kwdset(cs, +1);
544 free(cs);
545 if (vi_search('n', cnt, row, off))
546 return -1;
547 break;
548 case ' ':
549 for (i = 0; i < cnt; i++)
550 if (vi_nextoff(xb, +1, row, off))
551 break;
552 break;
553 case 127:
554 case TK_CTL('h'):
555 for (i = 0; i < cnt; i++)
556 if (vi_nextoff(xb, -1, row, off))
557 break;
558 break;
559 case '`':
560 if ((mark = vi_read()) <= 0)
561 return -1;
562 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
563 return -1;
564 *row = mark_row;
565 *off = mark_off;
566 break;
567 case '%':
568 if (lbuf_pair(xb, row, off))
569 return -1;
570 break;
571 default:
572 vi_back(mv);
573 return 0;
575 return mv;
578 static void swap(int *a, int *b)
580 int t = *a;
581 *a = *b;
582 *b = t;
585 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
587 struct sbuf *sb;
588 char *s1, *s2, *s3;
589 if (r1 == r2)
590 return uc_sub(lbuf_get(lb, r1), o1, o2);
591 sb = sbuf_make();
592 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
593 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
594 s2 = lbuf_cp(lb, r1 + 1, r2);
595 sbuf_str(sb, s1);
596 sbuf_str(sb, s2);
597 sbuf_str(sb, s3);
598 free(s1);
599 free(s2);
600 free(s3);
601 return sbuf_done(sb);
604 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
606 char *region;
607 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
608 reg_put(vi_ybuf, region, lnmode);
609 free(region);
610 xrow = r1;
611 xoff = lnmode ? xoff : o1;
614 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
616 char *pref, *post;
617 char *region;
618 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
619 reg_put(vi_ybuf, region, lnmode);
620 free(region);
621 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
622 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
623 if (!lnmode) {
624 struct sbuf *sb = sbuf_make();
625 sbuf_str(sb, pref);
626 sbuf_str(sb, post);
627 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
628 sbuf_free(sb);
629 } else {
630 lbuf_edit(xb, NULL, r1, r2 + 1);
632 xrow = r1;
633 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
634 free(pref);
635 free(post);
638 static int linecount(char *s)
640 int n;
641 for (n = 0; s; n++)
642 if ((s = strchr(s, '\n')))
643 s++;
644 return n;
647 static int charcount(char *text, char *post)
649 int tlen = strlen(text);
650 int plen = strlen(post);
651 char *nl = text;
652 int i;
653 if (tlen < plen)
654 return 0;
655 for (i = 0; i < tlen - plen; i++)
656 if (text[i] == '\n')
657 nl = text + i + 1;
658 return uc_slen(nl) - uc_slen(post);
661 static char *vi_input(char *pref, char *post, int *row, int *off)
663 char *rep = led_input(pref, post, &xkmap, ex_filetype());
664 if (!rep)
665 return NULL;
666 *row = linecount(rep) - 1;
667 *off = charcount(rep, post) - 1;
668 if (*off < 0)
669 *off = 0;
670 return rep;
673 static char *vi_indents(char *ln)
675 struct sbuf *sb = sbuf_make();
676 while (xai && ln && (*ln == ' ' || *ln == '\t'))
677 sbuf_chr(sb, *ln++);
678 return sbuf_done(sb);
681 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
683 char *region;
684 int row, off;
685 char *rep;
686 char *pref, *post;
687 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
688 reg_put(vi_ybuf, region, lnmode);
689 free(region);
690 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
691 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
692 vi_drawrm(r1, r2, 0);
693 rep = vi_input(pref, post, &row, &off);
694 if (rep) {
695 lbuf_edit(xb, rep, r1, r2 + 1);
696 xrow = r1 + row - 1;
697 xoff = off;
698 free(rep);
700 free(pref);
701 free(post);
704 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
706 char *pref, *post;
707 char *region, *s;
708 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
709 s = region;
710 while (*s) {
711 int c = (unsigned char) s[0];
712 if (c <= 0x7f) {
713 if (cmd == 'u')
714 s[0] = tolower(c);
715 if (cmd == 'U')
716 s[0] = toupper(c);
717 if (cmd == '~')
718 s[0] = islower(c) ? toupper(c) : tolower(c);
720 s = uc_next(s);
722 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
723 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
724 if (!lnmode) {
725 struct sbuf *sb = sbuf_make();
726 sbuf_str(sb, pref);
727 sbuf_str(sb, region);
728 sbuf_str(sb, post);
729 lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
730 sbuf_free(sb);
731 } else {
732 lbuf_edit(xb, region, r1, r2 + 1);
734 xrow = r2;
735 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
736 free(region);
737 free(pref);
738 free(post);
741 static void vi_pipe(int r1, int r2)
743 char *text;
744 char *rep;
745 int kmap = 0;
746 char *cmd = vi_prompt("!", &kmap);
747 if (!cmd)
748 return;
749 text = lbuf_cp(xb, r1, r2 + 1);
750 rep = cmd_pipe(cmd, text, 1, 1);
751 if (rep)
752 lbuf_edit(xb, rep, r1, r2 + 1);
753 free(cmd);
754 free(text);
755 free(rep);
758 static void vi_shift(int r1, int r2, int dir)
760 struct sbuf *sb;
761 char *ln;
762 int i;
763 for (i = r1; i <= r2; i++) {
764 if (!(ln = lbuf_get(xb, i)))
765 continue;
766 sb = sbuf_make();
767 if (dir > 0)
768 sbuf_chr(sb, '\t');
769 else
770 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
771 sbuf_str(sb, ln);
772 lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
773 sbuf_free(sb);
775 xrow = r1;
776 xoff = lbuf_indents(xb, xrow);
779 static int vc_motion(int cmd)
781 int r1 = xrow, r2 = xrow; /* region rows */
782 int o1 = xoff, o2 = xoff; /* visual region columns */
783 int lnmode = 0; /* line-based region */
784 int mv;
785 vi_arg2 = vi_prefix();
786 if (vi_arg2 < 0)
787 return 1;
788 o1 = ren_noeol(lbuf_get(xb, r1), o1);
789 o2 = o1;
790 if ((mv = vi_motionln(&r2, cmd))) {
791 o2 = -1;
792 } else if (!(mv = vi_motion(&r2, &o2))) {
793 vi_read();
794 return 1;
796 if (mv < 0)
797 return 1;
798 lnmode = o2 < 0;
799 if (lnmode) {
800 o1 = 0;
801 o2 = lbuf_eol(xb, r2);
803 if (r1 > r2) {
804 swap(&r1, &r2);
805 swap(&o1, &o2);
807 if (r1 == r2 && o1 > o2)
808 swap(&o1, &o2);
809 o1 = ren_noeol(lbuf_get(xb, r1), o1);
810 if (!lnmode && strchr("fFtTeE%", mv))
811 if (o2 < lbuf_eol(xb, r2))
812 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
813 if (cmd == 'y')
814 vi_yank(r1, o1, r2, o2, lnmode);
815 if (cmd == 'd')
816 vi_delete(r1, o1, r2, o2, lnmode);
817 if (cmd == 'c')
818 vi_change(r1, o1, r2, o2, lnmode);
819 if (cmd == '~' || cmd == 'u' || cmd == 'U')
820 vi_case(r1, o1, r2, o2, lnmode, cmd);
821 if (cmd == '!')
822 vi_pipe(r1, r2);
823 if (cmd == '>' || cmd == '<')
824 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
825 return 0;
828 static int vc_insert(int cmd)
830 char *pref, *post;
831 char *ln = lbuf_get(xb, xrow);
832 int row, off = 0;
833 char *rep;
834 if (cmd == 'I')
835 xoff = lbuf_indents(xb, xrow);
836 if (cmd == 'A')
837 xoff = lbuf_eol(xb, xrow);
838 xoff = ren_noeol(ln, xoff);
839 if (cmd == 'o')
840 xrow += 1;
841 if (cmd == 'i' || cmd == 'I')
842 off = xoff;
843 if (cmd == 'a' || cmd == 'A')
844 off = xoff + 1;
845 if (ln && ln[0] == '\n')
846 off = 0;
847 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
848 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
849 vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
850 rep = vi_input(pref, post, &row, &off);
851 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
852 lbuf_edit(xb, "\n", 0, 0);
853 if (rep) {
854 lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
855 xrow += row - 1;
856 xoff = off;
857 free(rep);
859 free(pref);
860 free(post);
861 return !rep;
864 static int vc_put(int cmd)
866 int cnt = MAX(1, vi_arg1);
867 int lnmode;
868 char *buf = reg_get(vi_ybuf, &lnmode);
869 int i;
870 if (!buf) {
871 snprintf(vi_msg, sizeof(vi_msg), "yank buffer empty\n");
872 return 1;
874 if (lnmode) {
875 struct sbuf *sb = sbuf_make();
876 for (i = 0; i < cnt; i++)
877 sbuf_str(sb, buf);
878 if (!lbuf_len(xb))
879 lbuf_edit(xb, "\n", 0, 0);
880 if (cmd == 'p')
881 xrow++;
882 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
883 xoff = lbuf_indents(xb, xrow);
884 sbuf_free(sb);
885 } else {
886 struct sbuf *sb = sbuf_make();
887 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
888 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
889 char *s = uc_sub(ln, 0, off);
890 sbuf_str(sb, s);
891 free(s);
892 for (i = 0; i < cnt; i++)
893 sbuf_str(sb, buf);
894 s = uc_sub(ln, off, -1);
895 sbuf_str(sb, s);
896 free(s);
897 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
898 xoff = off + uc_slen(buf) * cnt - 1;
899 sbuf_free(sb);
901 return 0;
904 static int join_spaces(char *prev, char *next)
906 int prevlen = strlen(prev);
907 if (!prev[0])
908 return 0;
909 if (prev[prevlen - 1] == ' ' || next[0] == ')')
910 return 0;
911 return prev[prevlen - 1] == '.' ? 2 : 1;
914 static int vc_join(void)
916 struct sbuf *sb;
917 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
918 int beg = xrow;
919 int end = xrow + cnt;
920 int off = 0;
921 int i;
922 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
923 return 1;
924 sb = sbuf_make();
925 for (i = beg; i < end; i++) {
926 char *ln = lbuf_get(xb, i);
927 char *lnend = strchr(ln, '\n');
928 int spaces;
929 if (i > beg)
930 while (ln[0] == ' ' || ln[0] == '\t')
931 ln++;
932 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
933 off = uc_slen(sbuf_buf(sb));
934 while (spaces--)
935 sbuf_chr(sb, ' ');
936 sbuf_mem(sb, ln, lnend - ln);
938 sbuf_chr(sb, '\n');
939 lbuf_edit(xb, sbuf_buf(sb), beg, end);
940 xoff = off;
941 sbuf_free(sb);
942 return 0;
945 static int vi_scrollforeward(int cnt)
947 if (xtop >= lbuf_len(xb) - 1)
948 return 1;
949 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
950 xrow = MAX(xrow, xtop);
951 return 0;
954 static int vi_scrollbackward(int cnt)
956 if (xtop == 0)
957 return 1;
958 xtop = MAX(0, xtop - cnt);
959 xrow = MIN(xrow, xtop + xrows - 1);
960 return 0;
963 static void vc_status(void)
965 int col = vi_off2col(xb, xrow, xoff);
966 snprintf(vi_msg, sizeof(vi_msg),
967 "\"%s\"%c %d lines L%d C%d\n",
968 ex_path()[0] ? ex_path() : "unnamed",
969 lbuf_modified(xb) ? '*' : ' ',
970 lbuf_len(xb), xrow + 1,
971 ren_cursor(lbuf_get(xb, xrow), col) + 1);
974 static void vc_charinfo(void)
976 char *c = uc_chr(lbuf_get(xb, xrow), xoff);
977 if (c) {
978 char cbuf[8] = "";
979 memcpy(cbuf, c, uc_len(c));
980 snprintf(vi_msg, sizeof(vi_msg), "<%s> %04x\n", cbuf, uc_code(c));
984 static int vc_replace(void)
986 int cnt = MAX(1, vi_arg1);
987 char *cs = vi_char();
988 char *ln = lbuf_get(xb, xrow);
989 struct sbuf *sb;
990 char *pref, *post;
991 char *s;
992 int off, i;
993 if (!ln || !cs)
994 return 1;
995 off = ren_noeol(ln, xoff);
996 s = uc_chr(ln, off);
997 for (i = 0; s[0] != '\n' && i < cnt; i++)
998 s = uc_next(s);
999 if (i < cnt)
1000 return 1;
1001 pref = uc_sub(ln, 0, off);
1002 post = uc_sub(ln, off + cnt, -1);
1003 sb = sbuf_make();
1004 sbuf_str(sb, pref);
1005 for (i = 0; i < cnt; i++)
1006 sbuf_str(sb, cs);
1007 sbuf_str(sb, post);
1008 lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
1009 off += cnt - 1;
1010 xoff = off;
1011 sbuf_free(sb);
1012 free(pref);
1013 free(post);
1014 return 0;
1017 static char rep_cmd[4096]; /* the last command */
1018 static int rep_len;
1020 static void vc_repeat(void)
1022 int i;
1023 for (i = 0; i < MAX(1, vi_arg1); i++)
1024 term_push(rep_cmd, rep_len);
1027 static void vc_execute(void)
1029 static int exec_buf;
1030 int lnmode;
1031 int c = vi_read();
1032 char *buf;
1033 int i;
1034 if (TK_INT(c))
1035 return;
1036 if (c == '@')
1037 c = exec_buf;
1038 exec_buf = c;
1039 buf = reg_get(exec_buf, &lnmode);
1040 if (buf)
1041 for (i = 0; i < MAX(1, vi_arg1); i++)
1042 term_push(buf, strlen(buf));
1045 static void vi(void)
1047 int xcol;
1048 int mark;
1049 char *ln;
1050 int kmap = 0;
1051 xtop = MAX(0, xrow - xrows / 2);
1052 xoff = 0;
1053 xcol = vi_off2col(xb, xrow, xoff);
1054 vi_drawagain(xcol, 0);
1055 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow), xcol));
1056 while (!xquit) {
1057 int mod = 0; /* screen should be redrawn (1: the whole screen, 2: the current line) */
1058 int nrow = xrow;
1059 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
1060 int otop = xtop;
1061 int oleft = xleft;
1062 int orow = xrow;
1063 int mv, n;
1064 term_cmd(&n);
1065 vi_arg2 = 0;
1066 vi_ybuf = vi_yankbuf();
1067 vi_arg1 = vi_prefix();
1068 if (!vi_ybuf)
1069 vi_ybuf = vi_yankbuf();
1070 mv = vi_motion(&nrow, &noff);
1071 if (mv > 0) {
1072 if (strchr("\'`GHML/?{}[]nN", mv) ||
1073 (mv == '%' && noff < 0)) {
1074 lbuf_mark(xb, '\'', xrow, xoff);
1075 lbuf_mark(xb, '`', xrow, xoff);
1077 xrow = nrow;
1078 if (noff < 0 && !strchr("jk", mv))
1079 noff = lbuf_indents(xb, xrow);
1080 if (strchr("jk", mv))
1081 noff = vi_col2off(xb, xrow, xcol);
1082 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
1083 if (!strchr("|jk", mv))
1084 xcol = vi_off2col(xb, xrow, xoff);
1085 if (mv == '|')
1086 xcol = vi_pcol;
1087 } else if (mv == 0) {
1088 char *cmd;
1089 int c = vi_read();
1090 int k = 0;
1091 if (c <= 0)
1092 continue;
1093 lbuf_mark(xb, '*', xrow, xoff);
1094 switch (c) {
1095 case TK_CTL('b'):
1096 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
1097 break;
1098 xoff = lbuf_indents(xb, xrow);
1099 mod = 1;
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 mod = 1;
1106 break;
1107 case TK_CTL('e'):
1108 if (vi_scrollforeward(MAX(1, vi_arg1)))
1109 break;
1110 break;
1111 case TK_CTL('y'):
1112 if (vi_scrollbackward(MAX(1, vi_arg1)))
1113 break;
1114 break;
1115 case TK_CTL('u'):
1116 if (xrow == 0)
1117 break;
1118 if (vi_arg1)
1119 vi_scroll = vi_arg1;
1120 n = vi_scroll ? vi_scroll : xrows / 2;
1121 xrow = MAX(0, xrow - n);
1122 if (xtop > 0)
1123 xtop = MAX(0, xtop - n);
1124 xoff = lbuf_indents(xb, xrow);
1125 mod = 1;
1126 break;
1127 case TK_CTL('d'):
1128 if (xrow == lbuf_len(xb) - 1)
1129 break;
1130 if (vi_arg1)
1131 vi_scroll = vi_arg1;
1132 n = vi_scroll ? vi_scroll : xrows / 2;
1133 xrow = MIN(MAX(0, lbuf_len(xb) - 1), xrow + n);
1134 if (xtop < lbuf_len(xb) - xrows)
1135 xtop = MIN(lbuf_len(xb) - xrows, xtop + n);
1136 xoff = lbuf_indents(xb, xrow);
1137 mod = 1;
1138 break;
1139 case TK_CTL('z'):
1140 term_pos(xrows, 0);
1141 term_suspend();
1142 mod = 1;
1143 break;
1144 case 'u':
1145 if (!lbuf_undo(xb)) {
1146 lbuf_jump(xb, '*', &xrow, &xoff);
1147 mod = 1;
1148 } else {
1149 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1151 break;
1152 case TK_CTL('r'):
1153 if (!lbuf_redo(xb)) {
1154 lbuf_jump(xb, '*', &xrow, &xoff);
1155 mod = 1;
1156 } else {
1157 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1159 break;
1160 case TK_CTL('g'):
1161 vc_status();
1162 break;
1163 case TK_CTL('^'):
1164 ex_command("e #");
1165 mod = 1;
1166 break;
1167 case ':':
1168 ln = vi_prompt(":", &kmap);
1169 if (ln && ln[0]) {
1170 ex_command(ln);
1171 mod = 1;
1173 free(ln);
1174 if (xquit)
1175 continue;
1176 break;
1177 case 'c':
1178 case 'd':
1179 case 'y':
1180 case '!':
1181 case '>':
1182 case '<':
1183 if (!vc_motion(c))
1184 mod = 1;
1185 break;
1186 case 'i':
1187 case 'I':
1188 case 'a':
1189 case 'A':
1190 case 'o':
1191 case 'O':
1192 if (!vc_insert(c))
1193 mod = 1;
1194 break;
1195 case 'J':
1196 if (!vc_join())
1197 mod = 1;
1198 break;
1199 case TK_CTL('l'):
1200 term_done();
1201 term_init();
1202 mod = 1;
1203 break;
1204 case 'm':
1205 if ((mark = vi_read()) > 0 && islower(mark))
1206 lbuf_mark(xb, mark, xrow, xoff);
1207 break;
1208 case 'p':
1209 case 'P':
1210 if (!vc_put(c))
1211 mod = 1;
1212 break;
1213 case 'z':
1214 k = vi_read();
1215 switch (k) {
1216 case '\n':
1217 xtop = vi_arg1 ? vi_arg1 : xrow;
1218 break;
1219 case '.':
1220 n = vi_arg1 ? vi_arg1 : xrow;
1221 xtop = MAX(0, n - xrows / 2);
1222 break;
1223 case '-':
1224 n = vi_arg1 ? vi_arg1 : xrow;
1225 xtop = MAX(0, n - xrows + 1);
1226 break;
1227 case 'l':
1228 case 'r':
1229 xtd = k == 'r' ? -1 : +1;
1230 break;
1231 case 'L':
1232 case 'R':
1233 xtd = k == 'R' ? -2 : +2;
1234 break;
1235 case 'e':
1236 case 'f':
1237 xkmap = k == 'e' ? 0 : xkmap_alt;
1238 break;
1240 mod = 1;
1241 break;
1242 case 'g':
1243 k = vi_read();
1244 if (k == '~' || k == 'u' || k == 'U')
1245 if (!vc_motion(k))
1246 mod = 2;
1247 if (k == 'a')
1248 vc_charinfo();
1249 break;
1250 case 'x':
1251 vi_back(' ');
1252 if (!vc_motion('d'))
1253 mod = 2;
1254 break;
1255 case 'X':
1256 vi_back(TK_CTL('h'));
1257 if (!vc_motion('d'))
1258 mod = 2;
1259 break;
1260 case 'C':
1261 vi_back('$');
1262 if (!vc_motion('c'))
1263 mod = 1;
1264 break;
1265 case 'D':
1266 vi_back('$');
1267 if (!vc_motion('d'))
1268 mod = 2;
1269 break;
1270 case 'r':
1271 if (!vc_replace())
1272 mod = 2;
1273 break;
1274 case 's':
1275 vi_back(' ');
1276 if (!vc_motion('c'))
1277 mod = 1;
1278 break;
1279 case 'S':
1280 vi_back('c');
1281 if (!vc_motion('c'))
1282 mod = 1;
1283 break;
1284 case 'Y':
1285 vi_back('y');
1286 vc_motion('y');
1287 break;
1288 case 'Z':
1289 k = vi_read();
1290 if (k == 'Z')
1291 ex_command("x");
1292 break;
1293 case '~':
1294 vi_back(' ');
1295 if (!vc_motion('~'))
1296 mod = 2;
1297 break;
1298 case '.':
1299 vc_repeat();
1300 break;
1301 case '@':
1302 vc_execute();
1303 break;
1304 default:
1305 continue;
1307 cmd = term_cmd(&n);
1308 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c) ||
1309 (c == 'g' && strchr("uU~", k))) {
1310 if (n < sizeof(rep_cmd)) {
1311 memcpy(rep_cmd, cmd, n);
1312 rep_len = n;
1316 if (xrow < 0 || xrow >= lbuf_len(xb))
1317 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1318 if (xtop > xrow)
1319 xtop = xtop - xrows / 2 > xrow ?
1320 MAX(0, xrow - xrows / 2) : xrow;
1321 if (xtop + xrows <= xrow)
1322 xtop = xtop + xrows + xrows / 2 <= xrow ?
1323 xrow - xrows / 2 : xrow - xrows + 1;
1324 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1325 if (mod)
1326 xcol = vi_off2col(xb, xrow, xoff);
1327 if (xcol >= xleft + xcols)
1328 xleft = xcol - xcols / 2;
1329 if (xcol < xleft)
1330 xleft = xcol < xcols ? 0 : xcol - xcols / 2;
1331 vi_wait();
1332 if (mod || xleft != oleft) {
1333 vi_drawagain(xcol, mod == 2 && xleft == oleft && xrow == orow);
1334 } else {
1335 if (xtop != otop)
1336 vi_drawupdate(xcol, otop);
1337 if (xhll && xrow != orow && orow >= xtop && orow < xtop + xrows)
1338 vi_drawrow(orow);
1339 if (xhll && xrow != orow)
1340 vi_drawrow(xrow);
1342 if (vi_msg[0])
1343 vi_drawmsg();
1344 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1345 ren_cursor(lbuf_get(xb, xrow), xcol)));
1346 lbuf_modified(xb);
1348 term_pos(xrows, 0);
1349 term_kill();
1352 int main(int argc, char *argv[])
1354 int i;
1355 char *prog = strchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
1356 xvis = strcmp("ex", prog) && strcmp("neatex", prog);
1357 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1358 if (argv[i][1] == 's')
1359 xled = 0;
1360 if (argv[i][1] == 'e')
1361 xvis = 0;
1362 if (argv[i][1] == 'v')
1363 xvis = 1;
1365 dir_init();
1366 syn_init();
1367 if (xled || xvis)
1368 term_init();
1369 if (!ex_init(argv + i)) {
1370 if (xvis)
1371 vi();
1372 else
1373 ex();
1374 ex_done();
1376 if (xled || xvis)
1377 term_done();
1378 reg_done();
1379 syn_done();
1380 dir_done();
1381 return 0;