vi: mark columns
[neatvi.git] / vi.c
blob56e6ac765a7011fe3cadbb789c5369710d164d63
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, mark_row, mark_off;
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)
235 return -1;
236 if (!islower(mark) && !strchr("'`", mark))
237 return -1;
238 if (lbuf_markpos(xb, mark, &mark_row, &mark_off))
239 return -1;
240 *row = mark_row;
241 break;
242 case 'j':
243 *row = MIN(*row + cnt, lbuf_len(xb) - 1);
244 break;
245 case 'k':
246 *row = MAX(*row - cnt, 0);
247 break;
248 case 'G':
249 *row = (vi_arg1 || vi_arg2) ? cnt - 1 : lbuf_len(xb) - 1;
250 break;
251 case 'H':
252 if (lbuf_len(xb))
253 *row = MIN(xtop + cnt - 1, lbuf_len(xb) - 1);
254 else
255 *row = 0;
256 break;
257 case 'L':
258 if (lbuf_len(xb))
259 *row = MIN(xtop + xrows - 1 - cnt + 1, lbuf_len(xb) - 1);
260 else
261 *row = 0;
262 break;
263 case 'M':
264 if (lbuf_len(xb))
265 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
266 else
267 *row = 0;
268 break;
269 default:
270 if (c == cmd) {
271 *row = MAX(0, MIN(*row + cnt - 1, lbuf_len(xb) - 1));
272 break;
274 vi_back(c);
275 return 0;
277 return c;
280 static char *vi_curword(struct lbuf *lb, int row, int off)
282 struct sbuf *sb;
283 char *ln = lbuf_get(lb, row);
284 char *beg, *end;
285 if (!ln)
286 return NULL;
287 beg = uc_chr(ln, ren_noeol(ln, off));
288 end = beg;
289 while (*end && uc_kind(end) == 1)
290 end = uc_next(end);
291 while (beg > ln && uc_kind(uc_beg(ln, beg - 1)) == 1)
292 beg = uc_beg(ln, beg - 1);
293 if (beg >= end)
294 return NULL;
295 sb = sbuf_make();
296 sbuf_str(sb, "\\<");
297 sbuf_mem(sb, beg, end - beg);
298 sbuf_str(sb, "\\>");
299 return sbuf_done(sb);
302 /* read a motion */
303 static int vi_motion(int *row, int *off)
305 int cnt = (vi_arg1 ? vi_arg1 : 1) * (vi_arg2 ? vi_arg2 : 1);
306 char *ln = lbuf_get(xb, *row);
307 int dir = dir_context(ln ? ln : "");
308 int mark, mark_row, mark_off;
309 char *cs;
310 int mv;
311 int i;
312 if ((mv = vi_motionln(row, 0))) {
313 *off = -1;
314 return mv;
316 mv = vi_read();
317 switch (mv) {
318 case 'f':
319 if (!(cs = vi_char()))
320 return -1;
321 if (vi_findchar(xb, cs, mv, cnt, row, off))
322 return -1;
323 break;
324 case 'F':
325 if (!(cs = vi_char()))
326 return -1;
327 if (vi_findchar(xb, cs, mv, cnt, row, off))
328 return -1;
329 break;
330 case ';':
331 if (!vi_charlast[0])
332 return -1;
333 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
334 return -1;
335 break;
336 case ',':
337 if (!vi_charlast[0])
338 return -1;
339 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
340 return -1;
341 break;
342 case 'h':
343 for (i = 0; i < cnt; i++)
344 if (vi_nextcol(xb, -1 * dir, row, off))
345 break;
346 break;
347 case 'l':
348 for (i = 0; i < cnt; i++)
349 if (vi_nextcol(xb, +1 * dir, row, off))
350 break;
351 break;
352 case 't':
353 if (!(cs = vi_char()))
354 return -1;
355 if (vi_findchar(xb, cs, mv, cnt, row, off))
356 return -1;
357 break;
358 case 'T':
359 if (!(cs = vi_char()))
360 return -1;
361 if (vi_findchar(xb, cs, mv, cnt, row, off))
362 return -1;
363 break;
364 case 'B':
365 for (i = 0; i < cnt; i++)
366 if (lbuf_wordend(xb, 1, -1, row, off))
367 break;
368 break;
369 case 'E':
370 for (i = 0; i < cnt; i++)
371 if (lbuf_wordend(xb, 1, +1, row, off))
372 break;
373 break;
374 case 'W':
375 for (i = 0; i < cnt; i++)
376 if (lbuf_wordbeg(xb, 1, +1, row, off))
377 break;
378 break;
379 case 'b':
380 for (i = 0; i < cnt; i++)
381 if (lbuf_wordend(xb, 0, -1, row, off))
382 break;
383 break;
384 case 'e':
385 for (i = 0; i < cnt; i++)
386 if (lbuf_wordend(xb, 0, +1, row, off))
387 break;
388 break;
389 case 'w':
390 for (i = 0; i < cnt; i++)
391 if (lbuf_wordbeg(xb, 0, +1, row, off))
392 break;
393 break;
394 case '{':
395 for (i = 0; i < cnt; i++)
396 if (lbuf_paragraphbeg(xb, -1, row, off))
397 break;
398 break;
399 case '}':
400 for (i = 0; i < cnt; i++)
401 if (lbuf_paragraphbeg(xb, +1, row, off))
402 break;
403 break;
404 case '[':
405 if (vi_read() != '[')
406 return -1;
407 for (i = 0; i < cnt; i++)
408 if (lbuf_sectionbeg(xb, -1, row, off))
409 break;
410 break;
411 case ']':
412 if (vi_read() != ']')
413 return -1;
414 for (i = 0; i < cnt; i++)
415 if (lbuf_sectionbeg(xb, +1, row, off))
416 break;
417 break;
418 case '0':
419 *off = 0;
420 break;
421 case '^':
422 *off = lbuf_indents(xb, *row);
423 break;
424 case '$':
425 *off = lbuf_eol(xb, *row);
426 break;
427 case '|':
428 *off = vi_col2off(xb, *row, cnt - 1);
429 vi_pcol = cnt - 1;
430 break;
431 case '/':
432 if (vi_search(mv, cnt, row, off))
433 return -1;
434 break;
435 case '?':
436 if (vi_search(mv, cnt, row, off))
437 return -1;
438 break;
439 case 'n':
440 if (vi_search(mv, cnt, row, off))
441 return -1;
442 break;
443 case 'N':
444 if (vi_search(mv, cnt, row, off))
445 return -1;
446 break;
447 case TK_CTL('a'):
448 if (!(cs = vi_curword(xb, *row, *off)))
449 return -1;
450 strcpy(vi_findlast, cs);
451 free(cs);
452 vi_finddir = +1;
453 if (vi_search('n', cnt, row, off))
454 return -1;
455 break;
456 case ' ':
457 for (i = 0; i < cnt; i++)
458 if (vi_nextoff(xb, +1, row, off))
459 break;
460 break;
461 case 127:
462 case TK_CTL('h'):
463 for (i = 0; i < cnt; i++)
464 if (vi_nextoff(xb, -1, row, off))
465 break;
466 break;
467 case '`':
468 if ((mark = vi_read()) <= 0)
469 return -1;
470 if (!islower(mark) && !strchr("'`", mark))
471 return -1;
472 if (lbuf_markpos(xb, mark, &mark_row, &mark_off))
473 return -1;
474 *row = mark_row;
475 *off = mark_off;
476 break;
477 default:
478 vi_back(mv);
479 return 0;
481 return mv;
484 static void swap(int *a, int *b)
486 int t = *a;
487 *a = *b;
488 *b = t;
491 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
493 struct sbuf *sb;
494 char *s1, *s2, *s3;
495 if (r1 == r2)
496 return uc_sub(lbuf_get(lb, r1), o1, o2);
497 sb = sbuf_make();
498 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
499 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
500 s2 = lbuf_cp(lb, r1 + 1, r2);
501 sbuf_str(sb, s1);
502 sbuf_str(sb, s2);
503 sbuf_str(sb, s3);
504 free(s1);
505 free(s2);
506 free(s3);
507 return sbuf_done(sb);
510 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
512 char *region;
513 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
514 reg_put(vi_ybuf, region, lnmode);
515 free(region);
516 xrow = r1;
517 xoff = lnmode ? xoff : o1;
520 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
522 char *pref, *post;
523 char *region;
524 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
525 reg_put(vi_ybuf, region, lnmode);
526 free(region);
527 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
528 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
529 lbuf_rm(xb, r1, r2 + 1);
530 if (!lnmode) {
531 struct sbuf *sb = sbuf_make();
532 sbuf_str(sb, pref);
533 sbuf_str(sb, post);
534 lbuf_put(xb, r1, sbuf_buf(sb));
535 sbuf_free(sb);
537 xrow = r1;
538 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
539 free(pref);
540 free(post);
543 static int linecount(char *s)
545 int n;
546 for (n = 0; s; n++)
547 if ((s = strchr(s, '\n')))
548 s++;
549 return n;
552 static int indentscopy(char *d, char *s, int len)
554 int i;
555 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
556 d[i] = s[i];
557 d[i] = '\0';
558 return i;
561 static char *vi_input(char *pref, char *post, int *row, int *off)
563 char ai[64] = "";
564 char *rep, *s;
565 struct sbuf *sb;
566 int last;
567 if (xai)
568 pref += indentscopy(ai, pref, sizeof(ai));
569 rep = led_input(pref, post, ai, xai ? sizeof(ai) - 1 : 0, &vi_kmap);
570 if (!rep)
571 return NULL;
572 sb = sbuf_make();
573 sbuf_str(sb, ai);
574 sbuf_str(sb, pref);
575 sbuf_str(sb, rep);
576 s = sbuf_buf(sb);
577 last = uc_lastline(s) - s;
578 *off = MAX(0, uc_slen(sbuf_buf(sb) + last) - 1);
579 if (last)
580 while (xai && (post[0] == ' ' || post[0] == '\t'))
581 post++;
582 sbuf_str(sb, post);
583 *row = linecount(sbuf_buf(sb)) - 1;
584 free(rep);
585 return sbuf_done(sb);
588 static char *vi_indents(char *ln)
590 struct sbuf *sb = sbuf_make();
591 while (xai && ln && (*ln == ' ' || *ln == '\t'))
592 sbuf_chr(sb, *ln++);
593 return sbuf_done(sb);
596 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
598 char *region;
599 int row, off;
600 char *rep;
601 char *pref, *post;
602 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
603 reg_put(vi_ybuf, region, lnmode);
604 free(region);
605 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
606 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
607 rep = vi_input(pref, post, &row, &off);
608 if (rep) {
609 lbuf_rm(xb, r1, r2 + 1);
610 lbuf_put(xb, r1, rep);
611 xrow = r1 + row - 1;
612 xoff = off;
613 free(rep);
615 free(pref);
616 free(post);
619 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
621 char *pref, *post;
622 char *region, *s;
623 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
624 s = region;
625 while (*s) {
626 int c = (unsigned char) s[0];
627 if (c <= 0x7f) {
628 if (cmd == 'u')
629 s[0] = tolower(c);
630 if (cmd == 'U')
631 s[0] = toupper(c);
632 if (cmd == '~')
633 s[0] = islower(c) ? toupper(c) : tolower(c);
635 s = uc_next(s);
637 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
638 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
639 lbuf_rm(xb, r1, r2 + 1);
640 if (!lnmode) {
641 struct sbuf *sb = sbuf_make();
642 sbuf_str(sb, pref);
643 sbuf_str(sb, region);
644 sbuf_str(sb, post);
645 lbuf_put(xb, r1, sbuf_buf(sb));
646 sbuf_free(sb);
647 } else {
648 lbuf_put(xb, r1, region);
650 xrow = r2;
651 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
652 free(region);
653 free(pref);
654 free(post);
657 static void vi_pipe(int r1, int r2)
659 char *text;
660 char *rep;
661 char *kmap = NULL;
662 char *cmd = vi_prompt("!", &kmap);
663 if (!cmd)
664 return;
665 text = lbuf_cp(xb, r1, r2 + 1);
666 rep = cmd_pipe(cmd, text);
667 if (rep) {
668 lbuf_rm(xb, r1, r2 + 1);
669 lbuf_put(xb, r1, rep);
671 free(cmd);
672 free(text);
673 free(rep);
676 static void vi_shift(int r1, int r2, int dir)
678 struct sbuf *sb;
679 char *ln;
680 int i;
681 for (i = r1; i <= r2; i++) {
682 if (!(ln = lbuf_get(xb, i)))
683 continue;
684 sb = sbuf_make();
685 if (dir > 0)
686 sbuf_chr(sb, '\t');
687 else
688 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
689 sbuf_str(sb, ln);
690 lbuf_rm(xb, i, i + 1);
691 lbuf_put(xb, i, sbuf_buf(sb));
692 sbuf_free(sb);
694 xrow = r1;
695 xoff = lbuf_indents(xb, xrow);
698 static int vc_motion(int cmd)
700 int r1 = xrow, r2 = xrow; /* region rows */
701 int o1 = xoff, o2 = xoff; /* visual region columns */
702 int lnmode = 0; /* line-based region */
703 int mv;
704 vi_arg2 = vi_prefix();
705 if (vi_arg2 < 0)
706 return 1;
707 o1 = ren_noeol(lbuf_get(xb, r1), o1);
708 o2 = o1;
709 if ((mv = vi_motionln(&r2, cmd))) {
710 o2 = -1;
711 } else if (!(mv = vi_motion(&r2, &o2))) {
712 vi_read();
713 return 1;
715 if (mv < 0)
716 return 1;
717 lnmode = o2 < 0;
718 if (lnmode) {
719 o1 = 0;
720 o2 = lbuf_eol(xb, r2);
722 if (r1 > r2) {
723 swap(&r1, &r2);
724 swap(&o1, &o2);
726 if (r1 == r2 && o1 > o2)
727 swap(&o1, &o2);
728 o1 = ren_noeol(lbuf_get(xb, r1), o1);
729 if (!lnmode && strchr("fFtTeE", mv))
730 if (o2 < lbuf_eol(xb, r2))
731 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
732 if (cmd == 'y')
733 vi_yank(r1, o1, r2, o2, lnmode);
734 if (cmd == 'd')
735 vi_delete(r1, o1, r2, o2, lnmode);
736 if (cmd == 'c')
737 vi_change(r1, o1, r2, o2, lnmode);
738 if (cmd == '~' || cmd == 'u' || cmd == 'U')
739 vi_case(r1, o1, r2, o2, lnmode, cmd);
740 if (cmd == '!')
741 vi_pipe(r1, r2);
742 if (cmd == '>' || cmd == '<')
743 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
744 return 0;
747 static int vc_insert(int cmd)
749 char *pref, *post;
750 char *ln = lbuf_get(xb, xrow);
751 int row, off = 0;
752 char *rep;
753 if (cmd == 'I')
754 xoff = lbuf_indents(xb, xrow);
755 if (cmd == 'A')
756 xoff = lbuf_eol(xb, xrow);
757 xoff = ren_noeol(ln, xoff);
758 if (cmd == 'o')
759 xrow += 1;
760 if (cmd == 'i' || cmd == 'I')
761 off = xoff;
762 if (cmd == 'a' || cmd == 'A')
763 off = xoff + 1;
764 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
765 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
766 rep = vi_input(pref, post, &row, &off);
767 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
768 lbuf_put(xb, 0, "\n");
769 if (rep) {
770 if (cmd != 'o' && cmd != 'O')
771 lbuf_rm(xb, xrow, xrow + 1);
772 lbuf_put(xb, xrow, rep);
773 xrow += row - 1;
774 xoff = off;
775 free(rep);
777 free(pref);
778 free(post);
779 return !rep;
782 static int vc_put(int cmd)
784 int cnt = MAX(1, vi_arg1);
785 int lnmode;
786 char *buf = reg_get(vi_ybuf, &lnmode);
787 int i;
788 if (!buf)
789 return 1;
790 if (lnmode) {
791 struct sbuf *sb = sbuf_make();
792 for (i = 0; i < cnt; i++)
793 sbuf_str(sb, buf);
794 if (!lbuf_len(xb))
795 lbuf_put(xb, 0, "\n");
796 if (cmd == 'p')
797 xrow++;
798 lbuf_put(xb, xrow, sbuf_buf(sb));
799 xoff = lbuf_indents(xb, xrow);
800 sbuf_free(sb);
801 } else {
802 struct sbuf *sb = sbuf_make();
803 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
804 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
805 char *s = uc_sub(ln, 0, off);
806 sbuf_str(sb, s);
807 free(s);
808 for (i = 0; i < cnt; i++)
809 sbuf_str(sb, buf);
810 s = uc_sub(ln, off, -1);
811 sbuf_str(sb, s);
812 free(s);
813 lbuf_rm(xb, xrow, xrow + 1);
814 lbuf_put(xb, xrow, sbuf_buf(sb));
815 xoff = off + uc_slen(buf) * cnt - 1;
816 sbuf_free(sb);
818 return 0;
821 static int join_spaces(char *prev, char *next)
823 int prevlen = strlen(prev);
824 if (!prev[0])
825 return 0;
826 if (prev[prevlen - 1] == ' ' || next[0] == ')')
827 return 0;
828 return prev[prevlen - 1] == '.' ? 2 : 1;
831 static int vc_join(void)
833 struct sbuf *sb;
834 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
835 int beg = xrow;
836 int end = xrow + cnt;
837 int off = 0;
838 int i;
839 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
840 return 1;
841 sb = sbuf_make();
842 for (i = beg; i < end; i++) {
843 char *ln = lbuf_get(xb, i);
844 char *lnend = strchr(ln, '\n');
845 int spaces;
846 if (i > beg)
847 while (ln[0] == ' ' || ln[0] == '\t')
848 ln++;
849 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
850 off = uc_slen(sbuf_buf(sb));
851 while (spaces--)
852 sbuf_chr(sb, ' ');
853 sbuf_mem(sb, ln, lnend - ln);
855 sbuf_chr(sb, '\n');
856 lbuf_rm(xb, beg, end);
857 lbuf_put(xb, beg, sbuf_buf(sb));
858 xoff = off;
859 sbuf_free(sb);
860 return 0;
863 static int vi_scrollforeward(int cnt)
865 if (xtop >= lbuf_len(xb) - 1)
866 return 1;
867 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
868 xrow = MAX(xrow, xtop);
869 return 0;
872 static int vi_scrollbackward(int cnt)
874 if (xtop == 0)
875 return 1;
876 xtop = MAX(0, xtop - cnt);
877 xrow = MIN(xrow, xtop + xrows - 1);
878 return 0;
881 static void vc_status(void)
883 int col = vi_off2col(xb, xrow, xoff);
884 snprintf(vi_msg, sizeof(vi_msg), "\"%s\" line %d of %d, col %d\n",
885 xpath[0] ? xpath : "unnamed", xrow + 1, lbuf_len(xb),
886 ren_cursor(lbuf_get(xb, xrow), col) + 1);
889 static int vc_replace(void)
891 int cnt = MAX(1, vi_arg1);
892 char *cs = vi_char();
893 char *ln = lbuf_get(xb, xrow);
894 struct sbuf *sb;
895 char *pref, *post;
896 char *s;
897 int off, i;
898 if (!ln || !cs)
899 return 1;
900 off = ren_noeol(ln, xoff);
901 s = uc_chr(ln, off);
902 for (i = 0; s[0] != '\n' && i < cnt; i++)
903 s = uc_next(s);
904 if (i < cnt)
905 return 1;
906 pref = uc_sub(ln, 0, off);
907 post = uc_sub(ln, off + cnt, -1);
908 sb = sbuf_make();
909 sbuf_str(sb, pref);
910 for (i = 0; i < cnt; i++)
911 sbuf_str(sb, cs);
912 sbuf_str(sb, post);
913 lbuf_rm(xb, xrow, xrow + 1);
914 lbuf_put(xb, xrow, sbuf_buf(sb));
915 off += cnt - 1;
916 xoff = off;
917 sbuf_free(sb);
918 free(pref);
919 free(post);
920 return 0;
923 static char rep_cmd[4096]; /* the last command */
924 static int rep_len;
926 static void vc_repeat(void)
928 term_push(rep_cmd, rep_len);
931 static void vc_execute(void)
933 static int exec_buf;
934 int lnmode;
935 int c = vi_read();
936 char *buf;
937 if (TK_INT(c))
938 return;
939 if (c == '@')
940 c = exec_buf;
941 exec_buf = c;
942 buf = reg_get(exec_buf, &lnmode);
943 if (buf)
944 term_push(buf, strlen(buf));
947 static void vi(void)
949 int xcol;
950 int mark;
951 char *ln;
952 char *kmap = NULL;
953 term_init();
954 xtop = 0;
955 xrow = 0;
956 xoff = 0;
957 xcol = vi_off2col(xb, xrow, xoff);
958 vi_draw(xcol);
959 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
960 while (!xquit) {
961 int redraw = 0;
962 int nrow = xrow;
963 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
964 int otop = xtop;
965 int mv, n;
966 term_cmd(&n);
967 vi_arg2 = 0;
968 vi_ybuf = vi_yankbuf();
969 vi_arg1 = vi_prefix();
970 if (!vi_ybuf)
971 vi_ybuf = vi_yankbuf();
972 mv = vi_motion(&nrow, &noff);
973 if (mv > 0) {
974 if (strchr("\'`GHML/?{}[]nN", mv)) {
975 lbuf_mark(xb, '\'', xrow, xoff);
976 lbuf_mark(xb, '`', xrow, xoff);
978 xrow = nrow;
979 if (noff < 0 && !strchr("jk", mv))
980 noff = lbuf_indents(xb, xrow);
981 if (strchr("jk", mv))
982 noff = vi_col2off(xb, xrow, xcol);
983 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
984 if (!strchr("|jk", mv))
985 xcol = vi_off2col(xb, xrow, xoff);
986 if (mv == '|')
987 xcol = vi_pcol;
988 } else if (mv == 0) {
989 char *cmd;
990 int c = vi_read();
991 int z, g;
992 if (c <= 0)
993 continue;
994 switch (c) {
995 case TK_CTL('b'):
996 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
997 break;
998 xoff = lbuf_indents(xb, xrow);
999 redraw = 1;
1000 break;
1001 case TK_CTL('f'):
1002 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1003 break;
1004 xoff = lbuf_indents(xb, xrow);
1005 redraw = 1;
1006 break;
1007 case TK_CTL('e'):
1008 if (vi_scrollforeward(MAX(1, vi_arg1)))
1009 break;
1010 redraw = 1;
1011 break;
1012 case TK_CTL('y'):
1013 if (vi_scrollbackward(MAX(1, vi_arg1)))
1014 break;
1015 redraw = 1;
1016 break;
1017 case TK_CTL('z'):
1018 term_pos(xrows, 0);
1019 term_suspend();
1020 redraw = 1;
1021 break;
1022 case 'u':
1023 lbuf_undo(xb);
1024 redraw = 1;
1025 break;
1026 case TK_CTL('r'):
1027 lbuf_redo(xb);
1028 redraw = 1;
1029 break;
1030 case TK_CTL('g'):
1031 vc_status();
1032 break;
1033 case TK_CTL('^'):
1034 ex_command("e #");
1035 redraw = 1;
1036 break;
1037 case ':':
1038 ln = vi_prompt(":", &kmap);
1039 if (ln && ln[0]) {
1040 ex_command(ln);
1041 redraw = 1;
1043 free(ln);
1044 if (xquit)
1045 continue;
1046 break;
1047 case 'c':
1048 case 'd':
1049 case 'y':
1050 case '!':
1051 case '>':
1052 case '<':
1053 if (!vc_motion(c))
1054 redraw = 1;
1055 break;
1056 case 'i':
1057 case 'I':
1058 case 'a':
1059 case 'A':
1060 case 'o':
1061 case 'O':
1062 if (!vc_insert(c))
1063 redraw = 1;
1064 break;
1065 case 'J':
1066 if (!vc_join())
1067 redraw = 1;
1068 break;
1069 case 'm':
1070 if ((mark = vi_read()) > 0 && islower(mark))
1071 lbuf_mark(xb, mark, xrow, xoff);
1072 break;
1073 case 'p':
1074 case 'P':
1075 if (!vc_put(c))
1076 redraw = 1;
1077 break;
1078 case 'z':
1079 z = vi_read();
1080 switch (z) {
1081 case '\n':
1082 xtop = vi_arg1 ? vi_arg1 : xrow;
1083 break;
1084 case '.':
1085 n = vi_arg1 ? vi_arg1 : xrow;
1086 xtop = MAX(0, n - xrows / 2);
1087 break;
1088 case '-':
1089 n = vi_arg1 ? vi_arg1 : xrow;
1090 xtop = MAX(0, n - xrows + 1);
1091 break;
1092 case 'l':
1093 case 'r':
1094 xdir = z == 'r' ? -1 : +1;
1095 break;
1096 case 'L':
1097 case 'R':
1098 xdir = z == 'R' ? -2 : +2;
1099 break;
1101 redraw = 1;
1102 break;
1103 case 'g':
1104 g = vi_read();
1105 if (g == '~' || g == 'u' || g == 'U')
1106 if (!vc_motion(g))
1107 redraw = 1;
1108 break;
1109 case 'x':
1110 vi_back(' ');
1111 if (!vc_motion('d'))
1112 redraw = 1;
1113 break;
1114 case 'X':
1115 vi_back(TK_CTL('h'));
1116 if (!vc_motion('d'))
1117 redraw = 1;
1118 break;
1119 case 'C':
1120 vi_back('$');
1121 if (!vc_motion('c'))
1122 redraw = 1;
1123 break;
1124 case 'D':
1125 vi_back('$');
1126 if (!vc_motion('d'))
1127 redraw = 1;
1128 break;
1129 case 'r':
1130 if (!vc_replace())
1131 redraw = 1;
1132 break;
1133 case 's':
1134 vi_back(' ');
1135 if (!vc_motion('c'))
1136 redraw = 1;
1137 break;
1138 case 'S':
1139 vi_back('c');
1140 if (!vc_motion('c'))
1141 redraw = 1;
1142 break;
1143 case 'Y':
1144 vi_back('y');
1145 if (!vc_motion('y'))
1146 redraw = 1;
1147 break;
1148 case '~':
1149 vi_back(' ');
1150 if (!vc_motion('~'))
1151 redraw = 1;
1152 break;
1153 case '.':
1154 vc_repeat();
1155 break;
1156 case '@':
1157 vc_execute();
1158 break;
1159 default:
1160 continue;
1162 cmd = term_cmd(&n);
1163 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1164 if (n < sizeof(rep_cmd)) {
1165 memcpy(rep_cmd, cmd, n);
1166 rep_len = n;
1170 if (xrow < 0 || xrow >= lbuf_len(xb))
1171 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1172 if (xtop > xrow)
1173 xtop = xtop - xrows / 2 > xrow ?
1174 MAX(0, xrow - xrows / 2) : xrow;
1175 if (xtop + xrows <= xrow)
1176 xtop = xtop + xrows + xrows / 2 <= xrow ?
1177 xrow - xrows / 2 : xrow - xrows + 1;
1178 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1179 if (redraw)
1180 xcol = vi_off2col(xb, xrow, xoff);
1181 if (redraw || xtop != otop)
1182 vi_draw(xcol);
1183 if (vi_msg[0])
1184 vi_drawmsg();
1185 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1186 ren_cursor(lbuf_get(xb, xrow), xcol)));
1187 lbuf_undomark(xb);
1189 term_pos(xrows, 0);
1190 term_kill();
1191 term_done();
1194 int main(int argc, char *argv[])
1196 char ecmd[PATHLEN];
1197 int i;
1198 xb = lbuf_make();
1199 xvis = 1;
1200 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1201 if (argv[i][1] == 's')
1202 xled = 0;
1203 if (argv[i][1] == 'e')
1204 xvis = 0;
1205 if (argv[i][1] == 'v')
1206 xvis = 1;
1208 dir_init();
1209 syn_init();
1210 if (i < argc) {
1211 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1212 ex_command(ecmd);
1214 if (xvis)
1215 vi();
1216 else
1217 ex();
1218 lbuf_free(xb);
1219 reg_done();
1220 syn_done();
1221 dir_done();
1222 return 0;