vi: move xoff before EOL after motions
[neatvi.git] / vi.c
blobc2bf264bb296dc26e88afc71f0a7c53c792b03c9
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 *ln;
774 char *buf = reg_get(vi_ybuf, &lnmode);
775 struct sbuf *sb;
776 int off;
777 int i;
778 if (!buf)
779 return 1;
780 ln = lnmode ? NULL : lbuf_get(xb, xrow);
781 off = ren_noeol(ln, xoff) + (cmd == 'p');
782 if (cmd == 'p' && !ln)
783 xrow++;
784 sb = sbuf_make();
785 if (ln) {
786 char *s = uc_sub(ln, 0, off);
787 sbuf_str(sb, s);
788 free(s);
790 for (i = 0; i < cnt; i++)
791 sbuf_str(sb, buf);
792 if (ln) {
793 char *s = uc_sub(ln, off, -1);
794 sbuf_str(sb, s);
795 free(s);
797 if (!ln && !lbuf_len(xb))
798 lbuf_put(xb, 0, "\n");
799 if (ln)
800 lbuf_rm(xb, xrow, xrow + 1);
801 lbuf_put(xb, xrow, sbuf_buf(sb));
802 if (ln)
803 xoff = off + uc_slen(buf) * cnt - 1;
804 else
805 xoff = lbuf_indents(xb, xrow);
806 sbuf_free(sb);
807 return 0;
810 static int join_spaces(char *prev, char *next)
812 int prevlen = strlen(prev);
813 if (!prev[0])
814 return 0;
815 if (prev[prevlen - 1] == ' ' || next[0] == ')')
816 return 0;
817 return prev[prevlen - 1] == '.' ? 2 : 1;
820 static int vc_join(void)
822 struct sbuf *sb;
823 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
824 int beg = xrow;
825 int end = xrow + cnt;
826 int off = 0;
827 int i;
828 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
829 return 1;
830 sb = sbuf_make();
831 for (i = beg; i < end; i++) {
832 char *ln = lbuf_get(xb, i);
833 char *lnend = strchr(ln, '\n');
834 int spaces;
835 if (i > beg)
836 while (ln[0] == ' ' || ln[0] == '\t')
837 ln++;
838 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
839 off = uc_slen(sbuf_buf(sb));
840 while (spaces--)
841 sbuf_chr(sb, ' ');
842 sbuf_mem(sb, ln, lnend - ln);
844 sbuf_chr(sb, '\n');
845 lbuf_rm(xb, beg, end);
846 lbuf_put(xb, beg, sbuf_buf(sb));
847 xoff = off;
848 sbuf_free(sb);
849 return 0;
852 static int vi_scrollforeward(int cnt)
854 if (xtop >= lbuf_len(xb) - 1)
855 return 1;
856 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
857 xrow = MAX(xrow, xtop);
858 return 0;
861 static int vi_scrollbackward(int cnt)
863 if (xtop == 0)
864 return 1;
865 xtop = MAX(0, xtop - cnt);
866 xrow = MIN(xrow, xtop + xrows - 1);
867 return 0;
870 static void vc_status(void)
872 int col = vi_off2col(xb, xrow, xoff);
873 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" line %d of %d, col %d\n",
874 xpath[0] ? xpath : "unnamed", xrow + 1, lbuf_len(xb),
875 ren_cursor(lbuf_get(xb, xrow), col) + 1);
878 static int vc_replace(void)
880 int cnt = MAX(1, vi_arg1);
881 char *cs = vi_char();
882 char *ln = lbuf_get(xb, xrow);
883 struct sbuf *sb;
884 char *pref, *post;
885 char *s;
886 int off, i;
887 if (!ln || !cs)
888 return 1;
889 off = ren_noeol(ln, xoff);
890 s = uc_chr(ln, off);
891 for (i = 0; s[0] != '\n' && i < cnt; i++)
892 s = uc_next(s);
893 if (i < cnt)
894 return 1;
895 pref = uc_sub(ln, 0, off);
896 post = uc_sub(ln, off + cnt, -1);
897 sb = sbuf_make();
898 sbuf_str(sb, pref);
899 for (i = 0; i < cnt; i++)
900 sbuf_str(sb, cs);
901 sbuf_str(sb, post);
902 lbuf_rm(xb, xrow, xrow + 1);
903 lbuf_put(xb, xrow, sbuf_buf(sb));
904 off += cnt - 1;
905 xoff = off;
906 sbuf_free(sb);
907 free(pref);
908 free(post);
909 return 0;
912 static void vi(void)
914 int xcol;
915 int mark;
916 char *ln;
917 char *kmap = NULL;
918 term_init();
919 xtop = 0;
920 xrow = 0;
921 xoff = 0;
922 xcol = vi_off2col(xb, xrow, xoff);
923 vi_draw(xcol);
924 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
925 while (!xquit) {
926 int redraw = 0;
927 int nrow = xrow;
928 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
929 int otop = xtop;
930 int mv, n;
931 vi_arg2 = 0;
932 vi_ybuf = vi_yankbuf();
933 vi_arg1 = vi_prefix();
934 if (!vi_ybuf)
935 vi_ybuf = vi_yankbuf();
936 mv = vi_motion(&nrow, &noff);
937 if (mv > 0) {
938 if (strchr("\'GHML/?{}[]nN", mv))
939 lbuf_mark(xb, '\'', xrow);
940 xrow = nrow;
941 if (noff < 0 && !strchr("jk", mv))
942 noff = lbuf_indents(xb, xrow);
943 if (strchr("jk", mv))
944 noff = vi_col2off(xb, xrow, xcol);
945 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
946 if (!strchr("|jk", mv))
947 xcol = vi_off2col(xb, xrow, xoff);
948 if (mv == '|')
949 xcol = vi_pcol;
950 } else if (mv == 0) {
951 int c = vi_read();
952 int z, g;
953 if (c <= 0)
954 continue;
955 switch (c) {
956 case TK_CTL('b'):
957 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
958 break;
959 xoff = lbuf_indents(xb, xrow);
960 redraw = 1;
961 break;
962 case TK_CTL('f'):
963 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
964 break;
965 xoff = lbuf_indents(xb, xrow);
966 redraw = 1;
967 break;
968 case TK_CTL('e'):
969 if (vi_scrollforeward(MAX(1, vi_arg1)))
970 break;
971 redraw = 1;
972 break;
973 case TK_CTL('y'):
974 if (vi_scrollbackward(MAX(1, vi_arg1)))
975 break;
976 redraw = 1;
977 break;
978 case 'u':
979 lbuf_undo(xb);
980 redraw = 1;
981 break;
982 case TK_CTL('r'):
983 lbuf_redo(xb);
984 redraw = 1;
985 break;
986 case TK_CTL('g'):
987 vc_status();
988 break;
989 case TK_CTL('^'):
990 ex_command("e #");
991 redraw = 1;
992 break;
993 case ':':
994 ln = vi_prompt(":", &kmap);
995 if (ln && ln[0]) {
996 ex_command(ln);
997 redraw = 1;
999 free(ln);
1000 if (xquit)
1001 continue;
1002 break;
1003 case 'c':
1004 case 'd':
1005 case 'y':
1006 case '!':
1007 case '>':
1008 case '<':
1009 if (!vc_motion(c))
1010 redraw = 1;
1011 break;
1012 case 'i':
1013 case 'I':
1014 case 'a':
1015 case 'A':
1016 case 'o':
1017 case 'O':
1018 if (!vc_insert(c))
1019 redraw = 1;
1020 break;
1021 case 'J':
1022 if (!vc_join())
1023 redraw = 1;
1024 break;
1025 case 'm':
1026 if ((mark = vi_read()) > 0 && isalpha(mark))
1027 lbuf_mark(xb, mark, xrow);
1028 break;
1029 case 'p':
1030 case 'P':
1031 if (!vc_put(c))
1032 redraw = 1;
1033 break;
1034 case 'z':
1035 z = vi_read();
1036 switch (z) {
1037 case '\n':
1038 xtop = vi_arg1 ? vi_arg1 : xrow;
1039 break;
1040 case '.':
1041 n = vi_arg1 ? vi_arg1 : xrow;
1042 xtop = MAX(0, n - xrows / 2);
1043 break;
1044 case '-':
1045 n = vi_arg1 ? vi_arg1 : xrow;
1046 xtop = MAX(0, n - xrows + 1);
1047 break;
1048 case 'l':
1049 case 'r':
1050 xdir = z == 'r' ? -1 : +1;
1051 break;
1052 case 'L':
1053 case 'R':
1054 xdir = z == 'R' ? -2 : +2;
1055 break;
1057 redraw = 1;
1058 break;
1059 case 'g':
1060 g = vi_read();
1061 if (g == '~' || g == 'u' || g == 'U')
1062 if (!vc_motion(g))
1063 redraw = 1;
1064 break;
1065 case 'x':
1066 vi_back(' ');
1067 if (!vc_motion('d'))
1068 redraw = 1;
1069 break;
1070 case 'X':
1071 vi_back(TK_CTL('h'));
1072 if (!vc_motion('d'))
1073 redraw = 1;
1074 break;
1075 case 'C':
1076 vi_back('$');
1077 if (!vc_motion('c'))
1078 redraw = 1;
1079 break;
1080 case 'D':
1081 vi_back('$');
1082 if (!vc_motion('d'))
1083 redraw = 1;
1084 break;
1085 case 'r':
1086 if (!vc_replace())
1087 redraw = 1;
1088 break;
1089 case 's':
1090 vi_back(' ');
1091 if (!vc_motion('c'))
1092 redraw = 1;
1093 break;
1094 case 'S':
1095 vi_back('c');
1096 if (!vc_motion('c'))
1097 redraw = 1;
1098 break;
1099 case 'Y':
1100 vi_back('y');
1101 if (!vc_motion('y'))
1102 redraw = 1;
1103 break;
1104 case '~':
1105 vi_back(' ');
1106 if (!vc_motion('~'))
1107 redraw = 1;
1108 break;
1109 default:
1110 continue;
1113 if (xrow < 0 || xrow >= lbuf_len(xb))
1114 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1115 if (xtop > xrow)
1116 xtop = xtop - xrows / 2 > xrow ?
1117 MAX(0, xrow - xrows / 2) : xrow;
1118 if (xtop + xrows <= xrow)
1119 xtop = xtop + xrows + xrows / 2 <= xrow ?
1120 xrow - xrows / 2 : xrow - xrows + 1;
1121 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1122 if (redraw)
1123 xcol = vi_off2col(xb, xrow, xoff);
1124 if (redraw || xtop != otop)
1125 vi_draw(xcol);
1126 if (vi_msg[0])
1127 vi_drawmsg();
1128 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1129 ren_cursor(lbuf_get(xb, xrow), xcol)));
1130 lbuf_undomark(xb);
1132 term_pos(xrows, 0);
1133 term_kill();
1134 term_done();
1137 int main(int argc, char *argv[])
1139 char ecmd[PATHLEN];
1140 int i;
1141 xb = lbuf_make();
1142 xvis = 1;
1143 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1144 if (argv[i][1] == 's')
1145 xled = 0;
1146 if (argv[i][1] == 'e')
1147 xvis = 0;
1148 if (argv[i][1] == 'v')
1149 xvis = 1;
1151 dir_init();
1152 syn_init();
1153 if (i < argc) {
1154 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1155 ex_command(ecmd);
1157 if (xvis)
1158 vi();
1159 else
1160 ex();
1161 lbuf_free(xb);
1162 reg_done();
1163 syn_done();
1164 dir_done();
1165 return 0;