ex: edit command and unspecified alternate file
[neatvi.git] / vi.c
blob494c89bd6e9d83c2732fcc67ad2e56706163a2ea
1 /*
2 * neatvi editor
4 * Copyright (C) 2015 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include "vi.h"
15 char vi_msg[512]; /* current message */
16 static char vi_findlast[256]; /* the last searched keyword */
17 static int vi_finddir; /* the last search direction */
18 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
19 static int vi_charcmd; /* the character finding command */
20 static int vi_arg1, vi_arg2; /* the first and second arguments */
21 static int vi_ybuf; /* current yank buffer */
22 static char *vi_kmap; /* current insertion keymap */
23 static int vi_pcol; /* the column requested by | command */
25 static void vi_drawmsg(void)
27 led_print(vi_msg, xrows);
28 vi_msg[0] = '\0';
31 static void vi_draw(int xcol)
33 int i;
34 term_record();
35 for (i = xtop; i < xtop + xrows; i++) {
36 char *s = lbuf_get(xb, i);
37 led_print(s ? s : (i ? "~" : ""), i - xtop);
39 vi_drawmsg();
40 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
41 term_commit();
44 static int vi_buf[128];
45 static int vi_buflen;
47 static int vi_read(void)
49 return vi_buflen ? vi_buf[--vi_buflen] : term_read(1000);
52 static void vi_back(int c)
54 if (vi_buflen < sizeof(vi_buf))
55 vi_buf[vi_buflen++] = c;
58 static char *vi_char(void)
60 return led_read(&vi_kmap);
63 static char *vi_prompt(char *msg, char **kmap)
65 term_pos(xrows, led_pos(msg, 0));
66 term_kill();
67 return led_prompt(msg, "", kmap);
70 char *ex_read(char *msg)
72 struct sbuf *sb;
73 char c;
74 if (xled) {
75 char *s = led_prompt(msg, "", &vi_kmap);
76 if (s)
77 term_chr('\n');
78 return s;
80 sb = sbuf_make();
81 while ((c = getchar()) != EOF && c != '\n')
82 sbuf_chr(sb, c);
83 if (c == EOF) {
84 sbuf_free(sb);
85 return NULL;
87 return sbuf_done(sb);
90 void ex_show(char *msg)
92 if (xvis) {
93 snprintf(vi_msg, sizeof(vi_msg), "%s", msg);
94 } else if (xled) {
95 led_print(msg, -1);
96 term_chr('\n');
97 } else {
98 printf("%s", msg);
102 static int vi_yankbuf(void)
104 int c = vi_read();
105 if (c == '"')
106 return vi_read();
107 vi_back(c);
108 return 0;
111 static int vi_prefix(void)
113 int n = 0;
114 int c = vi_read();
115 if ((c >= '1' && c <= '9')) {
116 while (isdigit(c)) {
117 n = n * 10 + c - '0';
118 c = vi_read();
121 vi_back(c);
122 return n;
125 static int vi_col2off(struct lbuf *lb, int row, int col)
127 char *ln = lbuf_get(lb, row);
128 return ln ? ren_off(ln, col) : 0;
131 static int vi_off2col(struct lbuf *lb, int row, int off)
133 char *ln = lbuf_get(lb, row);
134 return ln ? ren_pos(ln, off) : 0;
137 static int vi_nextoff(struct lbuf *lb, int dir, int *row, int *off)
139 int o = *off + dir;
140 if (o < 0 || !lbuf_get(lb, *row) || o >= uc_slen(lbuf_get(lb, *row)))
141 return 1;
142 *off = o;
143 return 0;
146 static int vi_nextcol(struct lbuf *lb, int dir, int *row, int *off)
148 char *ln = lbuf_get(lb, *row);
149 int col = ln ? ren_pos(ln, *off) : 0;
150 int o = ln ? ren_next(ln, col, dir) : -1;
151 if (o < 0)
152 return -1;
153 *off = ren_off(ln, o);
154 return 0;
157 static int vi_findchar(struct lbuf *lb, char *cs, int cmd, int n, int *row, int *off)
159 strcpy(vi_charlast, cs);
160 vi_charcmd = cmd;
161 return lbuf_findchar(lb, cs, cmd, n, row, off);
164 static int vi_search(int cmd, int cnt, int *row, int *off)
166 int r = *row;
167 int o = *off;
168 int failed = 0;
169 int len = 0;
170 int i, dir;
171 char *soff = "";
172 if (cmd == '/' || cmd == '?') {
173 char sign[4] = {cmd};
174 char *kw = vi_prompt(sign, &vi_kmap);
175 if (!kw)
176 return 1;
177 vi_finddir = cmd == '/' ? +1 : -1;
178 if (kw[0])
179 snprintf(vi_findlast, sizeof(vi_findlast), "%s", kw);
180 if (strchr(vi_findlast, cmd)) {
181 soff = strchr(vi_findlast, cmd) + 1;
182 *strchr(vi_findlast, cmd) = '\0';
184 free(kw);
186 dir = cmd == 'N' ? -vi_finddir : vi_finddir;
187 if (!vi_findlast[0] || !lbuf_len(xb))
188 return 1;
189 o = *off;
190 for (i = 0; i < cnt; i++) {
191 if (lbuf_search(xb, vi_findlast, dir, &r, &o, &len)) {
192 failed = 1;
193 break;
195 if (i + 1 < cnt && cmd == '/')
196 o += len;
198 if (!failed) {
199 *row = r;
200 *off = o;
201 while (soff[0] && isspace((unsigned char) soff[0]))
202 soff++;
203 if (soff[0]) {
204 *off = -1;
205 if (*row + atoi(soff) < 0 || *row + atoi(soff) >= lbuf_len(xb))
206 failed = 1;
207 else
208 *row += atoi(soff);
211 if (failed)
212 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" not found\n", vi_findlast);
213 return failed;
216 /* read a line motion */
217 static int vi_motionln(int *row, int cmd)
219 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
220 int c = vi_read();
221 int mark;
222 switch (c) {
223 case '\n':
224 case '+':
225 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
226 break;
227 case '-':
228 *row = MAX(*row - cnt, 0);
229 break;
230 case '_':
231 *row = MIN(*row + cnt - 1, lbuf_len(xb) - 1);
232 break;
233 case '\'':
234 if ((mark = vi_read()) <= 0 || (!isalpha(mark) && mark != '\''))
235 return -1;
236 if (lbuf_markpos(xb, mark) < 0)
237 return -1;
238 *row = lbuf_markpos(xb, mark);
239 break;
240 case 'j':
241 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
242 break;
243 case 'k':
244 *row = MAX(*row - cnt, 0);
245 break;
246 case 'G':
247 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
248 break;
249 case 'H':
250 if (lbuf_len(xb))
251 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
252 else
253 *row = 0;
254 break;
255 case 'L':
256 if (lbuf_len(xb))
257 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
258 else
259 *row = 0;
260 break;
261 case 'M':
262 if (lbuf_len(xb))
263 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
264 else
265 *row = 0;
266 break;
267 default:
268 if (c == cmd) {
269 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
270 break;
272 vi_back(c);
273 return 0;
275 return c;
278 static char *vi_curword(struct lbuf *lb, int row, int off)
280 struct sbuf *sb;
281 char *ln = lbuf_get(lb, row);
282 char *beg, *end;
283 if (!ln)
284 return NULL;
285 beg = uc_chr(ln, ren_noeol(ln, off));
286 end = beg;
287 while (*end && uc_kind(end) == 1)
288 end = uc_next(end);
289 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
290 beg = uc_beg(ln, beg - 1);
291 if (beg >= end)
292 return NULL;
293 sb = sbuf_make();
294 sbuf_str(sb, "\\<");
295 sbuf_mem(sb, beg, end - beg);
296 sbuf_str(sb, "\\>");
297 return sbuf_done(sb);
300 /* read a motion */
301 static int vi_motion(int *row, int *off)
303 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
304 char *ln = lbuf_get(xb, *row);
305 int dir = dir_context(ln ? ln : "");
306 char *cs;
307 int mv;
308 int i;
309 if ((mv = vi_motionln(row, 0))) {
310 *off = -1;
311 return mv;
313 mv = vi_read();
314 switch (mv) {
315 case 'f':
316 if (!(cs = vi_char()))
317 return -1;
318 if (vi_findchar(xb, cs, mv, cnt, row, off))
319 return -1;
320 break;
321 case 'F':
322 if (!(cs = vi_char()))
323 return -1;
324 if (vi_findchar(xb, cs, mv, cnt, row, off))
325 return -1;
326 break;
327 case ';':
328 if (!vi_charlast[0])
329 return -1;
330 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
331 return -1;
332 break;
333 case ',':
334 if (!vi_charlast[0])
335 return -1;
336 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
337 return -1;
338 break;
339 case 'h':
340 for (i = 0; i < cnt; i++)
341 if (vi_nextcol(xb, -1 * dir, row, off))
342 break;
343 break;
344 case 'l':
345 for (i = 0; i < cnt; i++)
346 if (vi_nextcol(xb, +1 * dir, row, off))
347 break;
348 break;
349 case 't':
350 if (!(cs = vi_char()))
351 return -1;
352 if (vi_findchar(xb, cs, mv, cnt, row, off))
353 return -1;
354 break;
355 case 'T':
356 if (!(cs = vi_char()))
357 return -1;
358 if (vi_findchar(xb, cs, mv, cnt, row, off))
359 return -1;
360 break;
361 case 'B':
362 for (i = 0; i < cnt; i++)
363 if (lbuf_wordend(xb, 1, -1, row, off))
364 break;
365 break;
366 case 'E':
367 for (i = 0; i < cnt; i++)
368 if (lbuf_wordend(xb, 1, +1, row, off))
369 break;
370 break;
371 case 'W':
372 for (i = 0; i < cnt; i++)
373 if (lbuf_wordbeg(xb, 1, +1, row, off))
374 break;
375 break;
376 case 'b':
377 for (i = 0; i < cnt; i++)
378 if (lbuf_wordend(xb, 0, -1, row, off))
379 break;
380 break;
381 case 'e':
382 for (i = 0; i < cnt; i++)
383 if (lbuf_wordend(xb, 0, +1, row, off))
384 break;
385 break;
386 case 'w':
387 for (i = 0; i < cnt; i++)
388 if (lbuf_wordbeg(xb, 0, +1, row, off))
389 break;
390 break;
391 case '{':
392 for (i = 0; i < cnt; i++)
393 if (lbuf_paragraphbeg(xb, -1, row, off))
394 break;
395 break;
396 case '}':
397 for (i = 0; i < cnt; i++)
398 if (lbuf_paragraphbeg(xb, +1, row, off))
399 break;
400 break;
401 case '[':
402 if (vi_read() != '[')
403 return -1;
404 for (i = 0; i < cnt; i++)
405 if (lbuf_sectionbeg(xb, -1, row, off))
406 break;
407 break;
408 case ']':
409 if (vi_read() != ']')
410 return -1;
411 for (i = 0; i < cnt; i++)
412 if (lbuf_sectionbeg(xb, +1, row, off))
413 break;
414 break;
415 case '0':
416 *off = 0;
417 break;
418 case '^':
419 *off = lbuf_indents(xb, *row);
420 break;
421 case '$':
422 *off = lbuf_eol(xb, *row);
423 break;
424 case '|':
425 *off = vi_col2off(xb, *row, cnt - 1);
426 vi_pcol = cnt - 1;
427 break;
428 case '/':
429 if (vi_search(mv, cnt, row, off))
430 return -1;
431 break;
432 case '?':
433 if (vi_search(mv, cnt, row, off))
434 return -1;
435 break;
436 case 'n':
437 if (vi_search(mv, cnt, row, off))
438 return -1;
439 break;
440 case 'N':
441 if (vi_search(mv, cnt, row, off))
442 return -1;
443 break;
444 case TK_CTL('a'):
445 if (!(cs = vi_curword(xb, *row, *off)))
446 return -1;
447 strcpy(vi_findlast, cs);
448 free(cs);
449 vi_finddir = +1;
450 if (vi_search('n', cnt, row, off))
451 return -1;
452 break;
453 case ' ':
454 for (i = 0; i < cnt; i++)
455 if (vi_nextoff(xb, +1, row, off))
456 break;
457 break;
458 case 127:
459 case TK_CTL('h'):
460 for (i = 0; i < cnt; i++)
461 if (vi_nextoff(xb, -1, row, off))
462 break;
463 break;
464 default:
465 vi_back(mv);
466 return 0;
468 return mv;
471 static void swap(int *a, int *b)
473 int t = *a;
474 *a = *b;
475 *b = t;
478 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
480 struct sbuf *sb;
481 char *s1, *s2, *s3;
482 if (r1 == r2)
483 return uc_sub(lbuf_get(lb, r1), o1, o2);
484 sb = sbuf_make();
485 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
486 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
487 s2 = lbuf_cp(lb, r1 + 1, r2);
488 sbuf_str(sb, s1);
489 sbuf_str(sb, s2);
490 sbuf_str(sb, s3);
491 free(s1);
492 free(s2);
493 free(s3);
494 return sbuf_done(sb);
497 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
499 char *region;
500 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
501 reg_put(vi_ybuf, region, lnmode);
502 free(region);
503 xrow = r1;
504 xoff = lnmode ? xoff : o1;
507 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
509 char *pref, *post;
510 char *region;
511 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
512 reg_put(vi_ybuf, region, lnmode);
513 free(region);
514 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
515 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
516 lbuf_rm(xb, r1, r2 + 1);
517 if (!lnmode) {
518 struct sbuf *sb = sbuf_make();
519 sbuf_str(sb, pref);
520 sbuf_str(sb, post);
521 lbuf_put(xb, r1, sbuf_buf(sb));
522 sbuf_free(sb);
524 xrow = r1;
525 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
526 free(pref);
527 free(post);
530 static int linecount(char *s)
532 int n;
533 for (n = 0; s; n++)
534 if ((s = strchr(s, '\n')))
535 s++;
536 return n;
539 static int indentscopy(char *d, char *s, int len)
541 int i;
542 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
543 d[i] = s[i];
544 d[i] = '\0';
545 return i;
548 static char *vi_input(char *pref, char *post, int *row, int *off)
550 char ai[64] = "";
551 char *rep, *s;
552 struct sbuf *sb;
553 int last;
554 if (xai)
555 pref += indentscopy(ai, pref, sizeof(ai));
556 rep = led_input(pref, post, ai, xai ? sizeof(ai) - 1 : 0, &vi_kmap);
557 if (!rep)
558 return NULL;
559 sb = sbuf_make();
560 sbuf_str(sb, ai);
561 sbuf_str(sb, pref);
562 sbuf_str(sb, rep);
563 s = sbuf_buf(sb);
564 last = uc_lastline(s) - s;
565 *off = MAX(0, uc_slen(sbuf_buf(sb) + last) - 1);
566 if (last)
567 while (xai && (post[0] == ' ' || post[0] == '\t'))
568 post++;
569 sbuf_str(sb, post);
570 *row = linecount(sbuf_buf(sb)) - 1;
571 free(rep);
572 return sbuf_done(sb);
575 static char *vi_indents(char *ln)
577 struct sbuf *sb = sbuf_make();
578 while (xai && ln && (*ln == ' ' || *ln == '\t'))
579 sbuf_chr(sb, *ln++);
580 return sbuf_done(sb);
583 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
585 char *region;
586 int row, off;
587 char *rep;
588 char *pref, *post;
589 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
590 reg_put(vi_ybuf, region, lnmode);
591 free(region);
592 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
593 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
594 rep = vi_input(pref, post, &row, &off);
595 if (rep) {
596 lbuf_rm(xb, r1, r2 + 1);
597 lbuf_put(xb, r1, rep);
598 xrow = r1 + row - 1;
599 xoff = off;
600 free(rep);
602 free(pref);
603 free(post);
606 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
608 char *pref, *post;
609 char *region, *s;
610 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
611 s = region;
612 while (*s) {
613 int c = (unsigned char) s[0];
614 if (c <= 0x7f) {
615 if (cmd == 'u')
616 s[0] = tolower(c);
617 if (cmd == 'U')
618 s[0] = toupper(c);
619 if (cmd == '~')
620 s[0] = islower(c) ? toupper(c) : tolower(c);
622 s = uc_next(s);
624 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
625 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
626 lbuf_rm(xb, r1, r2 + 1);
627 if (!lnmode) {
628 struct sbuf *sb = sbuf_make();
629 sbuf_str(sb, pref);
630 sbuf_str(sb, region);
631 sbuf_str(sb, post);
632 lbuf_put(xb, r1, sbuf_buf(sb));
633 sbuf_free(sb);
634 } else {
635 lbuf_put(xb, r1, region);
637 xrow = r2;
638 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
639 free(region);
640 free(pref);
641 free(post);
644 static void vi_pipe(int r1, int r2)
646 char *text;
647 char *rep;
648 char *kmap = NULL;
649 char *cmd = vi_prompt("!", &kmap);
650 if (!cmd)
651 return;
652 text = lbuf_cp(xb, r1, r2 + 1);
653 rep = cmd_pipe(cmd, text);
654 if (rep) {
655 lbuf_rm(xb, r1, r2 + 1);
656 lbuf_put(xb, r1, rep);
658 free(cmd);
659 free(text);
660 free(rep);
663 static void vi_shift(int r1, int r2, int dir)
665 struct sbuf *sb;
666 char *ln;
667 int i;
668 for (i = r1; i <= r2; i++) {
669 if (!(ln = lbuf_get(xb, i)))
670 continue;
671 sb = sbuf_make();
672 if (dir > 0)
673 sbuf_chr(sb, '\t');
674 else
675 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
676 sbuf_str(sb, ln);
677 lbuf_rm(xb, i, i + 1);
678 lbuf_put(xb, i, sbuf_buf(sb));
679 sbuf_free(sb);
681 xrow = r1;
682 xoff = lbuf_indents(xb, xrow);
685 static int vc_motion(int cmd)
687 int r1 = xrow, r2 = xrow; /* region rows */
688 int o1 = xoff, o2 = xoff; /* visual region columns */
689 int lnmode = 0; /* line-based region */
690 int mv;
691 vi_arg2 = vi_prefix();
692 if (vi_arg2 < 0)
693 return 1;
694 o1 = ren_noeol(lbuf_get(xb, r1), o1);
695 o2 = o1;
696 if ((mv = vi_motionln(&r2, cmd))) {
697 o2 = -1;
698 } else if (!(mv = vi_motion(&r2, &o2))) {
699 vi_read();
700 return 1;
702 if (mv < 0)
703 return 1;
704 lnmode = o2 < 0;
705 if (lnmode) {
706 o1 = 0;
707 o2 = lbuf_eol(xb, r2);
709 if (r1 > r2) {
710 swap(&r1, &r2);
711 swap(&o1, &o2);
713 if (r1 == r2 && o1 > o2)
714 swap(&o1, &o2);
715 o1 = ren_noeol(lbuf_get(xb, r1), o1);
716 if (!lnmode && strchr("fFtTeE", mv))
717 if (o2 < lbuf_eol(xb, r2))
718 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
719 if (cmd == 'y')
720 vi_yank(r1, o1, r2, o2, lnmode);
721 if (cmd == 'd')
722 vi_delete(r1, o1, r2, o2, lnmode);
723 if (cmd == 'c')
724 vi_change(r1, o1, r2, o2, lnmode);
725 if (cmd == '~' || cmd == 'u' || cmd == 'U')
726 vi_case(r1, o1, r2, o2, lnmode, cmd);
727 if (cmd == '!')
728 vi_pipe(r1, r2);
729 if (cmd == '>' || cmd == '<')
730 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
731 return 0;
734 static int vc_insert(int cmd)
736 char *pref, *post;
737 char *ln = lbuf_get(xb, xrow);
738 int row, off = 0;
739 char *rep;
740 if (cmd == 'I')
741 xoff = lbuf_indents(xb, xrow);
742 if (cmd == 'A')
743 xoff = lbuf_eol(xb, xrow);
744 xoff = ren_noeol(ln, xoff);
745 if (cmd == 'o')
746 xrow += 1;
747 if (cmd == 'i' || cmd == 'I')
748 off = xoff;
749 if (cmd == 'a' || cmd == 'A')
750 off = xoff + 1;
751 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
752 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
753 rep = vi_input(pref, post, &row, &off);
754 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
755 lbuf_put(xb, 0, "\n");
756 if (rep) {
757 if (cmd != 'o' && cmd != 'O')
758 lbuf_rm(xb, xrow, xrow + 1);
759 lbuf_put(xb, xrow, rep);
760 xrow += row - 1;
761 xoff = off;
762 free(rep);
764 free(pref);
765 free(post);
766 return !rep;
769 static int vc_put(int cmd)
771 int cnt = MAX(1, vi_arg1);
772 int lnmode;
773 char *buf = reg_get(vi_ybuf, &lnmode);
774 int i;
775 if (!buf)
776 return 1;
777 if (lnmode) {
778 struct sbuf *sb = sbuf_make();
779 for (i = 0; i < cnt; i++)
780 sbuf_str(sb, buf);
781 if (!lbuf_len(xb))
782 lbuf_put(xb, 0, "\n");
783 if (cmd == 'p')
784 xrow++;
785 lbuf_put(xb, xrow, sbuf_buf(sb));
786 xoff = lbuf_indents(xb, xrow);
787 sbuf_free(sb);
788 } else {
789 struct sbuf *sb = sbuf_make();
790 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
791 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
792 char *s = uc_sub(ln, 0, off);
793 sbuf_str(sb, s);
794 free(s);
795 for (i = 0; i < cnt; i++)
796 sbuf_str(sb, buf);
797 s = uc_sub(ln, off, -1);
798 sbuf_str(sb, s);
799 free(s);
800 lbuf_rm(xb, xrow, xrow + 1);
801 lbuf_put(xb, xrow, sbuf_buf(sb));
802 xoff = off + uc_slen(buf) * cnt - 1;
803 sbuf_free(sb);
805 return 0;
808 static int join_spaces(char *prev, char *next)
810 int prevlen = strlen(prev);
811 if (!prev[0])
812 return 0;
813 if (prev[prevlen - 1] == ' ' || next[0] == ')')
814 return 0;
815 return prev[prevlen - 1] == '.' ? 2 : 1;
818 static int vc_join(void)
820 struct sbuf *sb;
821 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
822 int beg = xrow;
823 int end = xrow + cnt;
824 int off = 0;
825 int i;
826 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
827 return 1;
828 sb = sbuf_make();
829 for (i = beg; i < end; i++) {
830 char *ln = lbuf_get(xb, i);
831 char *lnend = strchr(ln, '\n');
832 int spaces;
833 if (i > beg)
834 while (ln[0] == ' ' || ln[0] == '\t')
835 ln++;
836 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
837 off = uc_slen(sbuf_buf(sb));
838 while (spaces--)
839 sbuf_chr(sb, ' ');
840 sbuf_mem(sb, ln, lnend - ln);
842 sbuf_chr(sb, '\n');
843 lbuf_rm(xb, beg, end);
844 lbuf_put(xb, beg, sbuf_buf(sb));
845 xoff = off;
846 sbuf_free(sb);
847 return 0;
850 static int vi_scrollforeward(int cnt)
852 if (xtop >= lbuf_len(xb) - 1)
853 return 1;
854 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
855 xrow = MAX(xrow, xtop);
856 return 0;
859 static int vi_scrollbackward(int cnt)
861 if (xtop == 0)
862 return 1;
863 xtop = MAX(0, xtop - cnt);
864 xrow = MIN(xrow, xtop + xrows - 1);
865 return 0;
868 static void vc_status(void)
870 int col = vi_off2col(xb, xrow, xoff);
871 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" line %d of %d, col %d\n",
872 xpath[0] ? xpath : "unnamed", xrow + 1, lbuf_len(xb),
873 ren_cursor(lbuf_get(xb, xrow), col) + 1);
876 static int vc_replace(void)
878 int cnt = MAX(1, vi_arg1);
879 char *cs = vi_char();
880 char *ln = lbuf_get(xb, xrow);
881 struct sbuf *sb;
882 char *pref, *post;
883 char *s;
884 int off, i;
885 if (!ln || !cs)
886 return 1;
887 off = ren_noeol(ln, xoff);
888 s = uc_chr(ln, off);
889 for (i = 0; s[0] != '\n' && i < cnt; i++)
890 s = uc_next(s);
891 if (i < cnt)
892 return 1;
893 pref = uc_sub(ln, 0, off);
894 post = uc_sub(ln, off + cnt, -1);
895 sb = sbuf_make();
896 sbuf_str(sb, pref);
897 for (i = 0; i < cnt; i++)
898 sbuf_str(sb, cs);
899 sbuf_str(sb, post);
900 lbuf_rm(xb, xrow, xrow + 1);
901 lbuf_put(xb, xrow, sbuf_buf(sb));
902 off += cnt - 1;
903 xoff = off;
904 sbuf_free(sb);
905 free(pref);
906 free(post);
907 return 0;
910 static void vi(void)
912 int xcol;
913 int mark;
914 char *ln;
915 char *kmap = NULL;
916 term_init();
917 xtop = 0;
918 xrow = 0;
919 xoff = 0;
920 xcol = vi_off2col(xb, xrow, xoff);
921 vi_draw(xcol);
922 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
923 while (!xquit) {
924 int redraw = 0;
925 int nrow = xrow;
926 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
927 int otop = xtop;
928 int mv, n;
929 vi_arg2 = 0;
930 vi_ybuf = vi_yankbuf();
931 vi_arg1 = vi_prefix();
932 if (!vi_ybuf)
933 vi_ybuf = vi_yankbuf();
934 mv = vi_motion(&nrow, &noff);
935 if (mv > 0) {
936 if (strchr("\'GHML/?{}[]nN", mv))
937 lbuf_mark(xb, '\'', xrow);
938 xrow = nrow;
939 if (noff < 0 && !strchr("jk", mv))
940 noff = lbuf_indents(xb, xrow);
941 if (strchr("jk", mv))
942 noff = vi_col2off(xb, xrow, xcol);
943 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
944 if (!strchr("|jk", mv))
945 xcol = vi_off2col(xb, xrow, xoff);
946 if (mv == '|')
947 xcol = vi_pcol;
948 } else if (mv == 0) {
949 int c = vi_read();
950 int z, g;
951 if (c <= 0)
952 continue;
953 switch (c) {
954 case TK_CTL('b'):
955 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
956 break;
957 xoff = lbuf_indents(xb, xrow);
958 redraw = 1;
959 break;
960 case TK_CTL('f'):
961 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
962 break;
963 xoff = lbuf_indents(xb, xrow);
964 redraw = 1;
965 break;
966 case TK_CTL('e'):
967 if (vi_scrollforeward(MAX(1, vi_arg1)))
968 break;
969 redraw = 1;
970 break;
971 case TK_CTL('y'):
972 if (vi_scrollbackward(MAX(1, vi_arg1)))
973 break;
974 redraw = 1;
975 break;
976 case TK_CTL('z'):
977 term_pos(xrows, 0);
978 term_suspend();
979 redraw = 1;
980 break;
981 case 'u':
982 lbuf_undo(xb);
983 redraw = 1;
984 break;
985 case TK_CTL('r'):
986 lbuf_redo(xb);
987 redraw = 1;
988 break;
989 case TK_CTL('g'):
990 vc_status();
991 break;
992 case TK_CTL('^'):
993 ex_command("e #");
994 redraw = 1;
995 break;
996 case ':':
997 ln = vi_prompt(":", &kmap);
998 if (ln && ln[0]) {
999 ex_command(ln);
1000 redraw = 1;
1002 free(ln);
1003 if (xquit)
1004 continue;
1005 break;
1006 case 'c':
1007 case 'd':
1008 case 'y':
1009 case '!':
1010 case '>':
1011 case '<':
1012 if (!vc_motion(c))
1013 redraw = 1;
1014 break;
1015 case 'i':
1016 case 'I':
1017 case 'a':
1018 case 'A':
1019 case 'o':
1020 case 'O':
1021 if (!vc_insert(c))
1022 redraw = 1;
1023 break;
1024 case 'J':
1025 if (!vc_join())
1026 redraw = 1;
1027 break;
1028 case 'm':
1029 if ((mark = vi_read()) > 0 && isalpha(mark))
1030 lbuf_mark(xb, mark, xrow);
1031 break;
1032 case 'p':
1033 case 'P':
1034 if (!vc_put(c))
1035 redraw = 1;
1036 break;
1037 case 'z':
1038 z = vi_read();
1039 switch (z) {
1040 case '\n':
1041 xtop = vi_arg1 ? vi_arg1 : xrow;
1042 break;
1043 case '.':
1044 n = vi_arg1 ? vi_arg1 : xrow;
1045 xtop = MAX(0, n - xrows / 2);
1046 break;
1047 case '-':
1048 n = vi_arg1 ? vi_arg1 : xrow;
1049 xtop = MAX(0, n - xrows + 1);
1050 break;
1051 case 'l':
1052 case 'r':
1053 xdir = z == 'r' ? -1 : +1;
1054 break;
1055 case 'L':
1056 case 'R':
1057 xdir = z == 'R' ? -2 : +2;
1058 break;
1060 redraw = 1;
1061 break;
1062 case 'g':
1063 g = vi_read();
1064 if (g == '~' || g == 'u' || g == 'U')
1065 if (!vc_motion(g))
1066 redraw = 1;
1067 break;
1068 case 'x':
1069 vi_back(' ');
1070 if (!vc_motion('d'))
1071 redraw = 1;
1072 break;
1073 case 'X':
1074 vi_back(TK_CTL('h'));
1075 if (!vc_motion('d'))
1076 redraw = 1;
1077 break;
1078 case 'C':
1079 vi_back('$');
1080 if (!vc_motion('c'))
1081 redraw = 1;
1082 break;
1083 case 'D':
1084 vi_back('$');
1085 if (!vc_motion('d'))
1086 redraw = 1;
1087 break;
1088 case 'r':
1089 if (!vc_replace())
1090 redraw = 1;
1091 break;
1092 case 's':
1093 vi_back(' ');
1094 if (!vc_motion('c'))
1095 redraw = 1;
1096 break;
1097 case 'S':
1098 vi_back('c');
1099 if (!vc_motion('c'))
1100 redraw = 1;
1101 break;
1102 case 'Y':
1103 vi_back('y');
1104 if (!vc_motion('y'))
1105 redraw = 1;
1106 break;
1107 case '~':
1108 vi_back(' ');
1109 if (!vc_motion('~'))
1110 redraw = 1;
1111 break;
1112 default:
1113 continue;
1116 if (xrow < 0 || xrow >= lbuf_len(xb))
1117 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1118 if (xtop > xrow)
1119 xtop = xtop - xrows / 2 > xrow ?
1120 MAX(0, xrow - xrows / 2) : xrow;
1121 if (xtop + xrows <= xrow)
1122 xtop = xtop + xrows + xrows / 2 <= xrow ?
1123 xrow - xrows / 2 : xrow - xrows + 1;
1124 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1125 if (redraw)
1126 xcol = vi_off2col(xb, xrow, xoff);
1127 if (redraw || xtop != otop)
1128 vi_draw(xcol);
1129 if (vi_msg[0])
1130 vi_drawmsg();
1131 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1132 ren_cursor(lbuf_get(xb, xrow), xcol)));
1133 lbuf_undomark(xb);
1135 term_pos(xrows, 0);
1136 term_kill();
1137 term_done();
1140 int main(int argc, char *argv[])
1142 char ecmd[PATHLEN];
1143 int i;
1144 xb = lbuf_make();
1145 xvis = 1;
1146 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1147 if (argv[i][1] == 's')
1148 xled = 0;
1149 if (argv[i][1] == 'e')
1150 xvis = 0;
1151 if (argv[i][1] == 'v')
1152 xvis = 1;
1154 dir_init();
1155 syn_init();
1156 if (i < argc) {
1157 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1158 ex_command(ecmd);
1160 if (xvis)
1161 vi();
1162 else
1163 ex();
1164 lbuf_free(xb);
1165 reg_done();
1166 syn_done();
1167 dir_done();
1168 return 0;