lbuf: report buffer modification
[neatvi.git] / vi.c
blobffc2a698335797cce25571095dcb856935cf4610
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 (lbuf_jump(xb, mark, &mark_row, &mark_off))
237 return -1;
238 *row = mark_row;
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 int mark, mark_row, mark_off;
307 char *cs;
308 int mv;
309 int i;
310 if ((mv = vi_motionln(row, 0))) {
311 *off = -1;
312 return mv;
314 mv = vi_read();
315 switch (mv) {
316 case 'f':
317 if (!(cs = vi_char()))
318 return -1;
319 if (vi_findchar(xb, cs, mv, cnt, row, off))
320 return -1;
321 break;
322 case 'F':
323 if (!(cs = vi_char()))
324 return -1;
325 if (vi_findchar(xb, cs, mv, cnt, row, off))
326 return -1;
327 break;
328 case ';':
329 if (!vi_charlast[0])
330 return -1;
331 if (vi_findchar(xb, vi_charlast, vi_charcmd, cnt, row, off))
332 return -1;
333 break;
334 case ',':
335 if (!vi_charlast[0])
336 return -1;
337 if (vi_findchar(xb, vi_charlast, vi_charcmd, -cnt, row, off))
338 return -1;
339 break;
340 case 'h':
341 for (i = 0; i < cnt; i++)
342 if (vi_nextcol(xb, -1 * dir, row, off))
343 break;
344 break;
345 case 'l':
346 for (i = 0; i < cnt; i++)
347 if (vi_nextcol(xb, +1 * dir, row, off))
348 break;
349 break;
350 case 't':
351 if (!(cs = vi_char()))
352 return -1;
353 if (vi_findchar(xb, cs, mv, cnt, row, off))
354 return -1;
355 break;
356 case 'T':
357 if (!(cs = vi_char()))
358 return -1;
359 if (vi_findchar(xb, cs, mv, cnt, row, off))
360 return -1;
361 break;
362 case 'B':
363 for (i = 0; i < cnt; i++)
364 if (lbuf_wordend(xb, 1, -1, row, off))
365 break;
366 break;
367 case 'E':
368 for (i = 0; i < cnt; i++)
369 if (lbuf_wordend(xb, 1, +1, row, off))
370 break;
371 break;
372 case 'W':
373 for (i = 0; i < cnt; i++)
374 if (lbuf_wordbeg(xb, 1, +1, row, off))
375 break;
376 break;
377 case 'b':
378 for (i = 0; i < cnt; i++)
379 if (lbuf_wordend(xb, 0, -1, row, off))
380 break;
381 break;
382 case 'e':
383 for (i = 0; i < cnt; i++)
384 if (lbuf_wordend(xb, 0, +1, row, off))
385 break;
386 break;
387 case 'w':
388 for (i = 0; i < cnt; i++)
389 if (lbuf_wordbeg(xb, 0, +1, row, off))
390 break;
391 break;
392 case '{':
393 for (i = 0; i < cnt; i++)
394 if (lbuf_paragraphbeg(xb, -1, row, off))
395 break;
396 break;
397 case '}':
398 for (i = 0; i < cnt; i++)
399 if (lbuf_paragraphbeg(xb, +1, row, off))
400 break;
401 break;
402 case '[':
403 if (vi_read() != '[')
404 return -1;
405 for (i = 0; i < cnt; i++)
406 if (lbuf_sectionbeg(xb, -1, row, off))
407 break;
408 break;
409 case ']':
410 if (vi_read() != ']')
411 return -1;
412 for (i = 0; i < cnt; i++)
413 if (lbuf_sectionbeg(xb, +1, row, off))
414 break;
415 break;
416 case '0':
417 *off = 0;
418 break;
419 case '^':
420 *off = lbuf_indents(xb, *row);
421 break;
422 case '$':
423 *off = lbuf_eol(xb, *row);
424 break;
425 case '|':
426 *off = vi_col2off(xb, *row, cnt - 1);
427 vi_pcol = cnt - 1;
428 break;
429 case '/':
430 if (vi_search(mv, cnt, row, off))
431 return -1;
432 break;
433 case '?':
434 if (vi_search(mv, cnt, row, off))
435 return -1;
436 break;
437 case 'n':
438 if (vi_search(mv, cnt, row, off))
439 return -1;
440 break;
441 case 'N':
442 if (vi_search(mv, cnt, row, off))
443 return -1;
444 break;
445 case TK_CTL('a'):
446 if (!(cs = vi_curword(xb, *row, *off)))
447 return -1;
448 strcpy(vi_findlast, cs);
449 free(cs);
450 vi_finddir = +1;
451 if (vi_search('n', cnt, row, off))
452 return -1;
453 break;
454 case ' ':
455 for (i = 0; i < cnt; i++)
456 if (vi_nextoff(xb, +1, row, off))
457 break;
458 break;
459 case 127:
460 case TK_CTL('h'):
461 for (i = 0; i < cnt; i++)
462 if (vi_nextoff(xb, -1, row, off))
463 break;
464 break;
465 case '`':
466 if ((mark = vi_read()) <= 0)
467 return -1;
468 if (lbuf_jump(xb, mark, &mark_row, &mark_off))
469 return -1;
470 *row = mark_row;
471 *off = mark_off;
472 break;
473 default:
474 vi_back(mv);
475 return 0;
477 return mv;
480 static void swap(int *a, int *b)
482 int t = *a;
483 *a = *b;
484 *b = t;
487 static char *lbuf_region(struct lbuf *lb, int r1, int o1, int r2, int o2)
489 struct sbuf *sb;
490 char *s1, *s2, *s3;
491 if (r1 == r2)
492 return uc_sub(lbuf_get(lb, r1), o1, o2);
493 sb = sbuf_make();
494 s1 = uc_sub(lbuf_get(lb, r1), o1, -1);
495 s3 = uc_sub(lbuf_get(lb, r2), 0, o2);
496 s2 = lbuf_cp(lb, r1 + 1, r2);
497 sbuf_str(sb, s1);
498 sbuf_str(sb, s2);
499 sbuf_str(sb, s3);
500 free(s1);
501 free(s2);
502 free(s3);
503 return sbuf_done(sb);
506 static void vi_yank(int r1, int o1, int r2, int o2, int lnmode)
508 char *region;
509 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
510 reg_put(vi_ybuf, region, lnmode);
511 free(region);
512 xrow = r1;
513 xoff = lnmode ? xoff : o1;
516 static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
518 char *pref, *post;
519 char *region;
520 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
521 reg_put(vi_ybuf, region, lnmode);
522 free(region);
523 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
524 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
525 lbuf_rm(xb, r1, r2 + 1);
526 if (!lnmode) {
527 struct sbuf *sb = sbuf_make();
528 sbuf_str(sb, pref);
529 sbuf_str(sb, post);
530 lbuf_put(xb, r1, sbuf_buf(sb));
531 sbuf_free(sb);
533 xrow = r1;
534 xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
535 free(pref);
536 free(post);
539 static int linecount(char *s)
541 int n;
542 for (n = 0; s; n++)
543 if ((s = strchr(s, '\n')))
544 s++;
545 return n;
548 static int indentscopy(char *d, char *s, int len)
550 int i;
551 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
552 d[i] = s[i];
553 d[i] = '\0';
554 return i;
557 static char *vi_input(char *pref, char *post, int *row, int *off)
559 char ai[64] = "";
560 char *rep, *s;
561 struct sbuf *sb;
562 int last;
563 if (xai)
564 pref += indentscopy(ai, pref, sizeof(ai));
565 rep = led_input(pref, post, ai, xai ? sizeof(ai) - 1 : 0, &vi_kmap);
566 if (!rep)
567 return NULL;
568 sb = sbuf_make();
569 sbuf_str(sb, ai);
570 sbuf_str(sb, pref);
571 sbuf_str(sb, rep);
572 s = sbuf_buf(sb);
573 last = uc_lastline(s) - s;
574 *off = MAX(0, uc_slen(sbuf_buf(sb) + last) - 1);
575 if (last)
576 while (xai && (post[0] == ' ' || post[0] == '\t'))
577 post++;
578 sbuf_str(sb, post);
579 *row = linecount(sbuf_buf(sb)) - 1;
580 free(rep);
581 return sbuf_done(sb);
584 static char *vi_indents(char *ln)
586 struct sbuf *sb = sbuf_make();
587 while (xai && ln && (*ln == ' ' || *ln == '\t'))
588 sbuf_chr(sb, *ln++);
589 return sbuf_done(sb);
592 static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
594 char *region;
595 int row, off;
596 char *rep;
597 char *pref, *post;
598 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
599 reg_put(vi_ybuf, region, lnmode);
600 free(region);
601 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, o1);
602 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
603 rep = vi_input(pref, post, &row, &off);
604 if (rep) {
605 lbuf_rm(xb, r1, r2 + 1);
606 lbuf_put(xb, r1, rep);
607 xrow = r1 + row - 1;
608 xoff = off;
609 free(rep);
611 free(pref);
612 free(post);
615 static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
617 char *pref, *post;
618 char *region, *s;
619 region = lbuf_region(xb, r1, lnmode ? 0 : o1, r2, lnmode ? -1 : o2);
620 s = region;
621 while (*s) {
622 int c = (unsigned char) s[0];
623 if (c <= 0x7f) {
624 if (cmd == 'u')
625 s[0] = tolower(c);
626 if (cmd == 'U')
627 s[0] = toupper(c);
628 if (cmd == '~')
629 s[0] = islower(c) ? toupper(c) : tolower(c);
631 s = uc_next(s);
633 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
634 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
635 lbuf_rm(xb, r1, r2 + 1);
636 if (!lnmode) {
637 struct sbuf *sb = sbuf_make();
638 sbuf_str(sb, pref);
639 sbuf_str(sb, region);
640 sbuf_str(sb, post);
641 lbuf_put(xb, r1, sbuf_buf(sb));
642 sbuf_free(sb);
643 } else {
644 lbuf_put(xb, r1, region);
646 xrow = r2;
647 xoff = lnmode ? lbuf_indents(xb, r2) : o2;
648 free(region);
649 free(pref);
650 free(post);
653 static void vi_pipe(int r1, int r2)
655 char *text;
656 char *rep;
657 char *kmap = NULL;
658 char *cmd = vi_prompt("!", &kmap);
659 if (!cmd)
660 return;
661 text = lbuf_cp(xb, r1, r2 + 1);
662 rep = cmd_pipe(cmd, text);
663 if (rep) {
664 lbuf_rm(xb, r1, r2 + 1);
665 lbuf_put(xb, r1, rep);
667 free(cmd);
668 free(text);
669 free(rep);
672 static void vi_shift(int r1, int r2, int dir)
674 struct sbuf *sb;
675 char *ln;
676 int i;
677 for (i = r1; i <= r2; i++) {
678 if (!(ln = lbuf_get(xb, i)))
679 continue;
680 sb = sbuf_make();
681 if (dir > 0)
682 sbuf_chr(sb, '\t');
683 else
684 ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
685 sbuf_str(sb, ln);
686 lbuf_rm(xb, i, i + 1);
687 lbuf_put(xb, i, sbuf_buf(sb));
688 sbuf_free(sb);
690 xrow = r1;
691 xoff = lbuf_indents(xb, xrow);
694 static int vc_motion(int cmd)
696 int r1 = xrow, r2 = xrow; /* region rows */
697 int o1 = xoff, o2 = xoff; /* visual region columns */
698 int lnmode = 0; /* line-based region */
699 int mv;
700 vi_arg2 = vi_prefix();
701 if (vi_arg2 < 0)
702 return 1;
703 o1 = ren_noeol(lbuf_get(xb, r1), o1);
704 o2 = o1;
705 if ((mv = vi_motionln(&r2, cmd))) {
706 o2 = -1;
707 } else if (!(mv = vi_motion(&r2, &o2))) {
708 vi_read();
709 return 1;
711 if (mv < 0)
712 return 1;
713 lnmode = o2 < 0;
714 if (lnmode) {
715 o1 = 0;
716 o2 = lbuf_eol(xb, r2);
718 if (r1 > r2) {
719 swap(&r1, &r2);
720 swap(&o1, &o2);
722 if (r1 == r2 && o1 > o2)
723 swap(&o1, &o2);
724 o1 = ren_noeol(lbuf_get(xb, r1), o1);
725 if (!lnmode && strchr("fFtTeE", mv))
726 if (o2 < lbuf_eol(xb, r2))
727 o2 = ren_noeol(lbuf_get(xb, r2), o2) + 1;
728 if (cmd == 'y')
729 vi_yank(r1, o1, r2, o2, lnmode);
730 if (cmd == 'd')
731 vi_delete(r1, o1, r2, o2, lnmode);
732 if (cmd == 'c')
733 vi_change(r1, o1, r2, o2, lnmode);
734 if (cmd == '~' || cmd == 'u' || cmd == 'U')
735 vi_case(r1, o1, r2, o2, lnmode, cmd);
736 if (cmd == '!')
737 vi_pipe(r1, r2);
738 if (cmd == '>' || cmd == '<')
739 vi_shift(r1, r2, cmd == '>' ? +1 : -1);
740 return 0;
743 static int vc_insert(int cmd)
745 char *pref, *post;
746 char *ln = lbuf_get(xb, xrow);
747 int row, off = 0;
748 char *rep;
749 if (cmd == 'I')
750 xoff = lbuf_indents(xb, xrow);
751 if (cmd == 'A')
752 xoff = lbuf_eol(xb, xrow);
753 xoff = ren_noeol(ln, xoff);
754 if (cmd == 'o')
755 xrow += 1;
756 if (cmd == 'i' || cmd == 'I')
757 off = xoff;
758 if (cmd == 'a' || cmd == 'A')
759 off = xoff + 1;
760 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
761 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
762 rep = vi_input(pref, post, &row, &off);
763 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
764 lbuf_put(xb, 0, "\n");
765 if (rep) {
766 if (cmd != 'o' && cmd != 'O')
767 lbuf_rm(xb, xrow, xrow + 1);
768 lbuf_put(xb, xrow, rep);
769 xrow += row - 1;
770 xoff = off;
771 free(rep);
773 free(pref);
774 free(post);
775 return !rep;
778 static int vc_put(int cmd)
780 int cnt = MAX(1, vi_arg1);
781 int lnmode;
782 char *buf = reg_get(vi_ybuf, &lnmode);
783 int i;
784 if (!buf)
785 return 1;
786 if (lnmode) {
787 struct sbuf *sb = sbuf_make();
788 for (i = 0; i < cnt; i++)
789 sbuf_str(sb, buf);
790 if (!lbuf_len(xb))
791 lbuf_put(xb, 0, "\n");
792 if (cmd == 'p')
793 xrow++;
794 lbuf_put(xb, xrow, sbuf_buf(sb));
795 xoff = lbuf_indents(xb, xrow);
796 sbuf_free(sb);
797 } else {
798 struct sbuf *sb = sbuf_make();
799 char *ln = xrow < lbuf_len(xb) ? lbuf_get(xb, xrow) : "\n";
800 int off = ren_noeol(ln, xoff) + (ln[0] != '\n' && cmd == 'p');
801 char *s = uc_sub(ln, 0, off);
802 sbuf_str(sb, s);
803 free(s);
804 for (i = 0; i < cnt; i++)
805 sbuf_str(sb, buf);
806 s = uc_sub(ln, off, -1);
807 sbuf_str(sb, s);
808 free(s);
809 lbuf_rm(xb, xrow, xrow + 1);
810 lbuf_put(xb, xrow, sbuf_buf(sb));
811 xoff = off + uc_slen(buf) * cnt - 1;
812 sbuf_free(sb);
814 return 0;
817 static int join_spaces(char *prev, char *next)
819 int prevlen = strlen(prev);
820 if (!prev[0])
821 return 0;
822 if (prev[prevlen - 1] == ' ' || next[0] == ')')
823 return 0;
824 return prev[prevlen - 1] == '.' ? 2 : 1;
827 static int vc_join(void)
829 struct sbuf *sb;
830 int cnt = vi_arg1 <= 1 ? 2 : vi_arg1;
831 int beg = xrow;
832 int end = xrow + cnt;
833 int off = 0;
834 int i;
835 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
836 return 1;
837 sb = sbuf_make();
838 for (i = beg; i < end; i++) {
839 char *ln = lbuf_get(xb, i);
840 char *lnend = strchr(ln, '\n');
841 int spaces;
842 if (i > beg)
843 while (ln[0] == ' ' || ln[0] == '\t')
844 ln++;
845 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
846 off = uc_slen(sbuf_buf(sb));
847 while (spaces--)
848 sbuf_chr(sb, ' ');
849 sbuf_mem(sb, ln, lnend - ln);
851 sbuf_chr(sb, '\n');
852 lbuf_rm(xb, beg, end);
853 lbuf_put(xb, beg, sbuf_buf(sb));
854 xoff = off;
855 sbuf_free(sb);
856 return 0;
859 static int vi_scrollforeward(int cnt)
861 if (xtop >= lbuf_len(xb) - 1)
862 return 1;
863 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
864 xrow = MAX(xrow, xtop);
865 return 0;
868 static int vi_scrollbackward(int cnt)
870 if (xtop == 0)
871 return 1;
872 xtop = MAX(0, xtop - cnt);
873 xrow = MIN(xrow, xtop + xrows - 1);
874 return 0;
877 static void vc_status(void)
879 int col = vi_off2col(xb, xrow, xoff);
880 snprintf(vi_msg, sizeof(vi_msg),
881 "\"%s\"%c %d lines L%d C%d\n",
882 xpath[0] ? xpath : "unnamed",
883 lbuf_modified(xb) ? '*' : ' ',
884 lbuf_len(xb), xrow + 1,
885 ren_cursor(lbuf_get(xb, xrow), col) + 1);
888 static int vc_replace(void)
890 int cnt = MAX(1, vi_arg1);
891 char *cs = vi_char();
892 char *ln = lbuf_get(xb, xrow);
893 struct sbuf *sb;
894 char *pref, *post;
895 char *s;
896 int off, i;
897 if (!ln || !cs)
898 return 1;
899 off = ren_noeol(ln, xoff);
900 s = uc_chr(ln, off);
901 for (i = 0; s[0] != '\n' && i < cnt; i++)
902 s = uc_next(s);
903 if (i < cnt)
904 return 1;
905 pref = uc_sub(ln, 0, off);
906 post = uc_sub(ln, off + cnt, -1);
907 sb = sbuf_make();
908 sbuf_str(sb, pref);
909 for (i = 0; i < cnt; i++)
910 sbuf_str(sb, cs);
911 sbuf_str(sb, post);
912 lbuf_rm(xb, xrow, xrow + 1);
913 lbuf_put(xb, xrow, sbuf_buf(sb));
914 off += cnt - 1;
915 xoff = off;
916 sbuf_free(sb);
917 free(pref);
918 free(post);
919 return 0;
922 static char rep_cmd[4096]; /* the last command */
923 static int rep_len;
925 static void vc_repeat(void)
927 term_push(rep_cmd, rep_len);
930 static void vc_execute(void)
932 static int exec_buf;
933 int lnmode;
934 int c = vi_read();
935 char *buf;
936 if (TK_INT(c))
937 return;
938 if (c == '@')
939 c = exec_buf;
940 exec_buf = c;
941 buf = reg_get(exec_buf, &lnmode);
942 if (buf)
943 term_push(buf, strlen(buf));
946 static void vi(void)
948 int xcol;
949 int mark;
950 char *ln;
951 char *kmap = NULL;
952 term_init();
953 xtop = 0;
954 xrow = 0;
955 xoff = 0;
956 xcol = vi_off2col(xb, xrow, xoff);
957 vi_draw(xcol);
958 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
959 while (!xquit) {
960 int redraw = 0;
961 int nrow = xrow;
962 int noff = ren_noeol(lbuf_get(xb, xrow), xoff);
963 int otop = xtop;
964 int mv, n;
965 term_cmd(&n);
966 vi_arg2 = 0;
967 vi_ybuf = vi_yankbuf();
968 vi_arg1 = vi_prefix();
969 if (!vi_ybuf)
970 vi_ybuf = vi_yankbuf();
971 mv = vi_motion(&nrow, &noff);
972 if (mv > 0) {
973 if (strchr("\'`GHML/?{}[]nN", mv)) {
974 lbuf_mark(xb, '\'', xrow, xoff);
975 lbuf_mark(xb, '`', xrow, xoff);
977 xrow = nrow;
978 if (noff < 0 && !strchr("jk", mv))
979 noff = lbuf_indents(xb, xrow);
980 if (strchr("jk", mv))
981 noff = vi_col2off(xb, xrow, xcol);
982 xoff = ren_noeol(lbuf_get(xb, xrow), noff);
983 if (!strchr("|jk", mv))
984 xcol = vi_off2col(xb, xrow, xoff);
985 if (mv == '|')
986 xcol = vi_pcol;
987 } else if (mv == 0) {
988 char *cmd;
989 int c = vi_read();
990 int z, g;
991 if (c <= 0)
992 continue;
993 switch (c) {
994 case TK_CTL('b'):
995 if (vi_scrollbackward(MAX(1, vi_arg1) * (xrows - 1)))
996 break;
997 xoff = lbuf_indents(xb, xrow);
998 redraw = 1;
999 break;
1000 case TK_CTL('f'):
1001 if (vi_scrollforeward(MAX(1, vi_arg1) * (xrows - 1)))
1002 break;
1003 xoff = lbuf_indents(xb, xrow);
1004 redraw = 1;
1005 break;
1006 case TK_CTL('e'):
1007 if (vi_scrollforeward(MAX(1, vi_arg1)))
1008 break;
1009 redraw = 1;
1010 break;
1011 case TK_CTL('y'):
1012 if (vi_scrollbackward(MAX(1, vi_arg1)))
1013 break;
1014 redraw = 1;
1015 break;
1016 case TK_CTL('z'):
1017 term_pos(xrows, 0);
1018 term_suspend();
1019 redraw = 1;
1020 break;
1021 case 'u':
1022 if (!lbuf_undo(xb)) {
1023 lbuf_jump(xb, '[', &xrow, &xoff);
1024 xoff = lbuf_indents(xb, xrow);
1025 redraw = 1;
1026 } else {
1027 snprintf(vi_msg, sizeof(vi_msg), "undo failed\n");
1029 break;
1030 case TK_CTL('r'):
1031 if (!lbuf_redo(xb)) {
1032 lbuf_jump(xb, '[', &xrow, &xoff);
1033 xoff = lbuf_indents(xb, xrow);
1034 redraw = 1;
1035 } else {
1036 snprintf(vi_msg, sizeof(vi_msg), "redo failed\n");
1038 break;
1039 case TK_CTL('g'):
1040 vc_status();
1041 break;
1042 case TK_CTL('^'):
1043 ex_command("e #");
1044 redraw = 1;
1045 break;
1046 case ':':
1047 ln = vi_prompt(":", &kmap);
1048 if (ln && ln[0]) {
1049 ex_command(ln);
1050 redraw = 1;
1052 free(ln);
1053 if (xquit)
1054 continue;
1055 break;
1056 case 'c':
1057 case 'd':
1058 case 'y':
1059 case '!':
1060 case '>':
1061 case '<':
1062 if (!vc_motion(c))
1063 redraw = 1;
1064 break;
1065 case 'i':
1066 case 'I':
1067 case 'a':
1068 case 'A':
1069 case 'o':
1070 case 'O':
1071 if (!vc_insert(c))
1072 redraw = 1;
1073 break;
1074 case 'J':
1075 if (!vc_join())
1076 redraw = 1;
1077 break;
1078 case 'm':
1079 if ((mark = vi_read()) > 0 && islower(mark))
1080 lbuf_mark(xb, mark, xrow, xoff);
1081 break;
1082 case 'p':
1083 case 'P':
1084 if (!vc_put(c))
1085 redraw = 1;
1086 break;
1087 case 'z':
1088 z = vi_read();
1089 switch (z) {
1090 case '\n':
1091 xtop = vi_arg1 ? vi_arg1 : xrow;
1092 break;
1093 case '.':
1094 n = vi_arg1 ? vi_arg1 : xrow;
1095 xtop = MAX(0, n - xrows / 2);
1096 break;
1097 case '-':
1098 n = vi_arg1 ? vi_arg1 : xrow;
1099 xtop = MAX(0, n - xrows + 1);
1100 break;
1101 case 'l':
1102 case 'r':
1103 xdir = z == 'r' ? -1 : +1;
1104 break;
1105 case 'L':
1106 case 'R':
1107 xdir = z == 'R' ? -2 : +2;
1108 break;
1110 redraw = 1;
1111 break;
1112 case 'g':
1113 g = vi_read();
1114 if (g == '~' || g == 'u' || g == 'U')
1115 if (!vc_motion(g))
1116 redraw = 1;
1117 break;
1118 case 'x':
1119 vi_back(' ');
1120 if (!vc_motion('d'))
1121 redraw = 1;
1122 break;
1123 case 'X':
1124 vi_back(TK_CTL('h'));
1125 if (!vc_motion('d'))
1126 redraw = 1;
1127 break;
1128 case 'C':
1129 vi_back('$');
1130 if (!vc_motion('c'))
1131 redraw = 1;
1132 break;
1133 case 'D':
1134 vi_back('$');
1135 if (!vc_motion('d'))
1136 redraw = 1;
1137 break;
1138 case 'r':
1139 if (!vc_replace())
1140 redraw = 1;
1141 break;
1142 case 's':
1143 vi_back(' ');
1144 if (!vc_motion('c'))
1145 redraw = 1;
1146 break;
1147 case 'S':
1148 vi_back('c');
1149 if (!vc_motion('c'))
1150 redraw = 1;
1151 break;
1152 case 'Y':
1153 vi_back('y');
1154 if (!vc_motion('y'))
1155 redraw = 1;
1156 break;
1157 case '~':
1158 vi_back(' ');
1159 if (!vc_motion('~'))
1160 redraw = 1;
1161 break;
1162 case '.':
1163 vc_repeat();
1164 break;
1165 case '@':
1166 vc_execute();
1167 break;
1168 default:
1169 continue;
1171 cmd = term_cmd(&n);
1172 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c)) {
1173 if (n < sizeof(rep_cmd)) {
1174 memcpy(rep_cmd, cmd, n);
1175 rep_len = n;
1179 if (xrow < 0 || xrow >= lbuf_len(xb))
1180 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1181 if (xtop > xrow)
1182 xtop = xtop - xrows / 2 > xrow ?
1183 MAX(0, xrow - xrows / 2) : xrow;
1184 if (xtop + xrows <= xrow)
1185 xtop = xtop + xrows + xrows / 2 <= xrow ?
1186 xrow - xrows / 2 : xrow - xrows + 1;
1187 xoff = ren_noeol(lbuf_get(xb, xrow), xoff);
1188 if (redraw)
1189 xcol = vi_off2col(xb, xrow, xoff);
1190 if (redraw || xtop != otop)
1191 vi_draw(xcol);
1192 if (vi_msg[0])
1193 vi_drawmsg();
1194 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1195 ren_cursor(lbuf_get(xb, xrow), xcol)));
1196 lbuf_modified(xb);
1198 term_pos(xrows, 0);
1199 term_kill();
1200 term_done();
1203 int main(int argc, char *argv[])
1205 char ecmd[PATHLEN];
1206 int i;
1207 xb = lbuf_make();
1208 xvis = 1;
1209 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1210 if (argv[i][1] == 's')
1211 xled = 0;
1212 if (argv[i][1] == 'e')
1213 xvis = 0;
1214 if (argv[i][1] == 'v')
1215 xvis = 1;
1217 dir_init();
1218 syn_init();
1219 if (i < argc) {
1220 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1221 ex_command(ecmd);
1223 if (xvis)
1224 vi();
1225 else
1226 ex();
1227 lbuf_free(xb);
1228 reg_done();
1229 syn_done();
1230 dir_done();
1231 return 0;