lbuf: increase the number of lines after moving the old ones
[neatvi.git] / vi.c
bloba0db33ad52f16fa561f6ae320e152917aa9127ad
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 xpath[PATHLEN]; /* current file */
16 char xpath_alt[PATHLEN]; /* alternate file */
17 struct lbuf *xb; /* current buffer */
18 int xrow, xcol, xtop; /* current row, column, and top row */
19 int xrow_alt; /* alternate row, column, and top row */
20 int xled = 1; /* use the line editor */
21 int xdir = 'L'; /* current direction context */
22 int xvis; /* visual mode */
23 int xquit;
24 int xautoindent = 1;
25 static char vi_findlast[256]; /* the last searched keyword */
26 static int vi_finddir; /* the last search direction */
27 static char vi_charlast[8]; /* the last character searched via f, t, F, or T */
28 static int vi_charcmd; /* the character finding command */
30 static void vi_draw(void)
32 int i;
33 term_record();
34 for (i = xtop; i < xtop + xrows; i++) {
35 char *s = lbuf_get(xb, i);
36 led_print(s ? s : "~", i - xtop);
38 led_print("", xrows);
39 term_pos(xrow, led_pos(lbuf_get(xb, i), xcol));
40 term_commit();
43 static int vi_buf[128];
44 static int vi_buflen;
46 static int vi_read(void)
48 return vi_buflen ? vi_buf[--vi_buflen] : term_read(1000);
51 static void vi_back(int c)
53 if (vi_buflen < sizeof(vi_buf))
54 vi_buf[vi_buflen++] = c;
57 static char *vi_char(void)
59 int key = vi_read();
60 return TK_INT(key) ? NULL : led_keymap(key);
63 static int vi_prefix(void)
65 int pre = 0;
66 int c = vi_read();
67 if ((c >= '1' && c <= '9')) {
68 while (isdigit(c)) {
69 pre = pre * 10 + c - '0';
70 c = vi_read();
73 vi_back(c);
74 return pre;
77 static int lbuf_lnnext(struct lbuf *lb, int *r, int *c, int dir)
79 char *ln = lbuf_get(lb, *r);
80 int col = ln ? ren_next(ln, *c, dir) : -1;
81 if (col < 0)
82 return -1;
83 *c = col;
84 return 0;
87 static void lbuf_eol(struct lbuf *lb, int *r, int *c, int dir)
89 char *ln = lbuf_get(lb, *r);
90 *c = dir < 0 ? 0 : MAX(0, ren_wid(ln ? ln : "") - 1);
93 static int lbuf_next(struct lbuf *lb, int *r, int *c, int dir)
95 if (dir < 0 && *r >= lbuf_len(lb))
96 *r = MAX(0, lbuf_len(lb) - 1);
97 if (lbuf_lnnext(lb, r, c, dir)) {
98 if (!lbuf_get(lb, *r + dir))
99 return -1;
100 *r += dir;
101 lbuf_eol(lb, r, c, -dir);
102 return 0;
104 return 0;
107 /* return a pointer to the character at visual position c of line r */
108 static char *lbuf_chr(struct lbuf *lb, int r, int c)
110 char *ln = lbuf_get(lb, r);
111 return ln ? uc_chr(ln, ren_off(ln, c)) : "";
114 static void lbuf_postindents(struct lbuf *lb, int *r, int *c)
116 lbuf_eol(lb, r, c, -1);
117 while (uc_isspace(lbuf_chr(lb, *r, *c)))
118 if (lbuf_lnnext(lb, r, c, +1))
119 break;
122 static int lbuf_findchar(struct lbuf *lb, int *row, int *col, char *cs, int cmd, int n)
124 int dir = (cmd == 'f' || cmd == 't') ? +1 : -1;
125 int c = *col;
126 if (n < 0)
127 dir = -dir;
128 if (n < 0)
129 n = -n;
130 strcpy(vi_charlast, cs);
131 vi_charcmd = cmd;
132 while (n > 0 && !lbuf_lnnext(lb, row, &c, dir))
133 if (uc_code(lbuf_chr(lb, *row, c)) == uc_code(cs))
134 n--;
135 if (!n)
136 *col = c;
137 if (!n && (cmd == 't' || cmd == 'T'))
138 lbuf_lnnext(lb, row, col, -dir);
139 return n != 0;
142 static int lbuf_search(struct lbuf *lb, char *kw, int dir, int *r, int *c, int *len)
144 int offs[2];
145 int found = 0;
146 int row = *r, col = *c;
147 int i;
148 struct rset *re = rset_make(1, &kw, 0);
149 if (!re)
150 return 1;
151 for (i = row; !found && i >= 0 && i < lbuf_len(lb); i += dir) {
152 char *s = lbuf_get(lb, i);
153 int off = dir > 0 && row == i ? uc_chr(s, col + 1) - s : 0;
154 int flg = off ? RE_NOTBOL : 0;
155 while (rset_find(re, s + off, 1, offs, flg) >= 0) {
156 if (dir < 0 && row == i && off + offs[0] >= col)
157 break;
158 found = 1;
159 *c = uc_off(s, off + offs[0]);
160 *r = i;
161 *len = offs[1] - offs[0];
162 off += offs[1];
163 if (dir > 0)
164 break;
167 rset_free(re);
168 return !found;
171 static int vi_search(int cmd, int cnt, int *row, int *col)
173 int r = *row;
174 int c = *col;
175 int failed = 0;
176 int len = 0;
177 int i, dir;
178 char *off = "";
179 if (cmd == '/' || cmd == '?') {
180 char sign[4] = {cmd};
181 char *kw;
182 term_pos(xrows, led_pos(sign, 0));
183 term_kill();
184 if (!(kw = led_prompt(sign, "")))
185 return 1;
186 vi_finddir = cmd == '/' ? +1 : -1;
187 if (kw[0])
188 snprintf(vi_findlast, sizeof(vi_findlast), "%s", kw);
189 if (strchr(vi_findlast, cmd)) {
190 off = strchr(vi_findlast, cmd) + 1;
191 *strchr(vi_findlast, cmd) = '\0';
193 free(kw);
195 dir = cmd == 'N' ? -vi_finddir : vi_finddir;
196 if (!vi_findlast[0] || !lbuf_len(xb))
197 return 1;
198 c = ren_off(lbuf_get(xb, *row), *col);
199 for (i = 0; i < cnt; i++) {
200 if (lbuf_search(xb, vi_findlast, dir, &r, &c, &len)) {
201 failed = 1;
202 break;
204 if (i + 1 < cnt && cmd == '/')
205 c += len;
207 if (!failed) {
208 *row = r;
209 *col = ren_pos(lbuf_get(xb, r), c);
210 while (off[0] && isspace((unsigned char) off[0]))
211 off++;
212 if (off[0]) {
213 *col = -1;
214 if (*row + atoi(off) < 0 || *row + atoi(off) >= lbuf_len(xb))
215 failed = 1;
216 else
217 *row += atoi(off);
220 return failed;
223 /* move to the last character of the word */
224 static int lbuf_wordlast(struct lbuf *lb, int *row, int *col, int kind, int dir)
226 if (!kind || !(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
227 return 0;
228 while (uc_kind(lbuf_chr(lb, *row, *col)) & kind)
229 if (lbuf_next(lb, row, col, dir))
230 return 1;
231 if (!(uc_kind(lbuf_chr(lb, *row, *col)) & kind))
232 lbuf_next(lb, row, col, -dir);
233 return 0;
236 static int lbuf_wordbeg(struct lbuf *lb, int *row, int *col, int big, int dir)
238 int nl = 0;
239 lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir);
240 if (lbuf_next(lb, row, col, dir))
241 return 1;
242 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
243 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
244 if (nl == 2)
245 return 0;
246 if (lbuf_next(lb, row, col, dir))
247 return 1;
249 return 0;
252 static int lbuf_wordend(struct lbuf *lb, int *row, int *col, int big, int dir)
254 int nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? -1 : 0;
255 if (!uc_isspace(lbuf_chr(lb, *row, *col)))
256 if (lbuf_next(lb, row, col, dir))
257 return 1;
258 while (uc_isspace(lbuf_chr(lb, *row, *col))) {
259 nl = uc_code(lbuf_chr(lb, *row, *col)) == '\n' ? nl + 1 : 0;
260 if (nl == 2) {
261 if (dir < 0)
262 lbuf_next(lb, row, col, -dir);
263 return 0;
265 if (lbuf_next(lb, row, col, dir))
266 return 1;
268 if (lbuf_wordlast(lb, row, col, big ? 3 : uc_kind(lbuf_chr(lb, *row, *col)), dir))
269 return 1;
270 return 0;
273 /* read a line motion */
274 static int vi_motionln(int *row, int cmd, int pre1, int pre2)
276 int pre = (pre1 ? pre1 : 1) * (pre2 ? pre2 : 1);
277 int c = vi_read();
278 int mark;
279 switch (c) {
280 case '\n':
281 case '+':
282 *row = MIN(*row + pre, lbuf_len(xb) - 1);
283 break;
284 case '-':
285 *row = MAX(*row - pre, 0);
286 break;
287 case '_':
288 *row = MIN(*row + pre - 1, lbuf_len(xb) - 1);
289 break;
290 case '\'':
291 if ((mark = vi_read()) > 0 && (isalpha(mark) || mark == '\''))
292 if (lbuf_markpos(xb, mark) >= 0)
293 *row = lbuf_markpos(xb, mark);
294 break;
295 case 'j':
296 *row = MIN(*row + pre, lbuf_len(xb) - 1);
297 break;
298 case 'k':
299 *row = MAX(*row - pre, 0);
300 break;
301 case 'G':
302 *row = (pre1 || pre2) ? pre - 1 : lbuf_len(xb) - 1;
303 break;
304 case 'H':
305 if (lbuf_len(xb))
306 *row = MIN(xtop + pre - 1, lbuf_len(xb) - 1);
307 else
308 *row = 0;
309 break;
310 case 'L':
311 if (lbuf_len(xb))
312 *row = MIN(xtop + xrows - 1 - pre + 1, lbuf_len(xb) - 1);
313 else
314 *row = 0;
315 break;
316 case 'M':
317 if (lbuf_len(xb))
318 *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
319 else
320 *row = 0;
321 break;
322 default:
323 if (c == cmd) {
324 *row = MIN(*row + pre - 1, lbuf_len(xb) - 1);
325 break;
327 vi_back(c);
328 return 0;
330 return c;
333 /* read a motion */
334 static int vi_motion(int *row, int *col, int pre1, int pre2)
336 int pre = (pre1 ? pre1 : 1) * (pre2 ? pre2 : 1);
337 char *ln = lbuf_get(xb, *row);
338 int dir = dir_context(ln ? ln : "");
339 char *cs;
340 int mv;
341 int i;
342 if ((mv = vi_motionln(row, 0, pre1, pre2))) {
343 *col = -1;
344 return mv;
346 mv = vi_read();
347 switch (mv) {
348 case ' ':
349 for (i = 0; i < pre; i++)
350 if (lbuf_lnnext(xb, row, col, 1))
351 break;
352 break;
353 case 'f':
354 if ((cs = vi_char()))
355 if (lbuf_findchar(xb, row, col, cs, mv, pre))
356 return -1;
357 break;
358 case 'F':
359 if ((cs = vi_char()))
360 if (lbuf_findchar(xb, row, col, cs, mv, pre))
361 return -1;
362 break;
363 case ';':
364 if (vi_charlast[0])
365 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, pre))
366 return -1;
367 break;
368 case ',':
369 if (vi_charlast[0])
370 if (lbuf_findchar(xb, row, col, vi_charlast, vi_charcmd, -pre))
371 return -1;
372 break;
373 case 'h':
374 for (i = 0; i < pre; i++)
375 if (lbuf_lnnext(xb, row, col, -1 * dir))
376 break;
377 break;
378 case 'l':
379 for (i = 0; i < pre; i++)
380 if (lbuf_lnnext(xb, row, col, +1 * dir))
381 break;
382 break;
383 case 't':
384 if ((cs = vi_char()))
385 if (lbuf_findchar(xb, row, col, cs, mv, pre))
386 return -1;
387 break;
388 case 'T':
389 if ((cs = vi_char()))
390 if (lbuf_findchar(xb, row, col, cs, mv, pre))
391 return -1;
392 break;
393 case 'B':
394 for (i = 0; i < pre; i++)
395 if (lbuf_wordend(xb, row, col, 1, -1))
396 break;
397 break;
398 case 'E':
399 for (i = 0; i < pre; i++)
400 if (lbuf_wordend(xb, row, col, 1, +1))
401 break;
402 break;
403 case 'W':
404 for (i = 0; i < pre; i++)
405 if (lbuf_wordbeg(xb, row, col, 1, +1))
406 break;
407 break;
408 case 'b':
409 for (i = 0; i < pre; i++)
410 if (lbuf_wordend(xb, row, col, 0, -1))
411 break;
412 break;
413 case 'e':
414 for (i = 0; i < pre; i++)
415 if (lbuf_wordend(xb, row, col, 0, +1))
416 break;
417 break;
418 case 'w':
419 for (i = 0; i < pre; i++)
420 if (lbuf_wordbeg(xb, row, col, 0, +1))
421 break;
422 break;
423 case '0':
424 lbuf_eol(xb, row, col, -1);
425 break;
426 case '^':
427 lbuf_postindents(xb, row, col);
428 break;
429 case '$':
430 lbuf_eol(xb, row, col, +1);
431 lbuf_lnnext(xb, row, col, -1);
432 break;
433 case '|':
434 *col = pre - 1;
435 break;
436 case '/':
437 if (vi_search(mv, pre, row, col))
438 return -1;
439 break;
440 case '?':
441 if (vi_search(mv, pre, row, col))
442 return -1;
443 break;
444 case 'n':
445 if (vi_search(mv, pre, row, col))
446 return -1;
447 break;
448 case 'N':
449 if (vi_search(mv, pre, row, col))
450 return -1;
451 break;
452 case 127:
453 case TK_CTL('h'):
454 for (i = 0; i < pre; i++)
455 if (lbuf_lnnext(xb, row, col, -1))
456 break;
457 break;
458 default:
459 vi_back(mv);
460 return 0;
462 return mv;
465 static void swap(int *a, int *b)
467 int t = *a;
468 *a = *b;
469 *b = t;
472 static char *lbuf_region(struct lbuf *lb, int r1, int l1, int r2, int l2)
474 struct sbuf *sb;
475 char *s1, *s2, *s3;
476 if (r1 == r2)
477 return uc_sub(lbuf_get(lb, r1), l1, l2);
478 sb = sbuf_make();
479 s1 = uc_sub(lbuf_get(lb, r1), l1, -1);
480 s3 = uc_sub(lbuf_get(lb, r2), 0, l2);
481 s2 = lbuf_cp(lb, r1 + 1, r2);
482 sbuf_str(sb, s1);
483 sbuf_str(sb, s2);
484 sbuf_str(sb, s3);
485 free(s1);
486 free(s2);
487 free(s3);
488 return sbuf_done(sb);
491 /* insertion offset before or after the given visual position */
492 static int vi_insertionoffset(char *s, int c1, int before)
494 int l1, l2, c2;
495 c2 = ren_next(s, c1, before ? -1 : +1);
496 l2 = c2 >= 0 ? ren_off(s, c2) : 0;
497 if (c1 == c2 || c2 < 0 || uc_chr(s, l2)[0] == '\n') {
498 c2 = ren_next(s, c1, before ? +1 : -1);
499 l1 = ren_off(s, c1);
500 l2 = c2 >= 0 ? ren_off(s, c2) : 0;
501 if (c1 == c2 || c2 < 0 || uc_chr(s, l2)[0] == '\n')
502 return before ? l1 : l1 + 1;
503 if (before)
504 return l1 < l2 ? l1 : l1 + 1;
505 else
506 return l2 < l1 ? l1 + 1 : l1;
508 ren_region(s, c1, c2, &l1, &l2, 0);
509 c1 = ren_pos(s, l1);
510 c2 = ren_pos(s, l2);
511 if (c1 < c2)
512 return l1 < l2 ? l2 : l1;
513 else
514 return l1 < l2 ? l1 : l2;
517 static void vi_commandregion(int *r1, int *r2, int *c1, int *c2, int *l1, int *l2, int closed)
519 if (*r2 < *r1 || (*r2 == *r1 && *c2 < *c1)) {
520 swap(r1, r2);
521 swap(c1, c2);
523 *l1 = lbuf_get(xb, *r1) ? vi_insertionoffset(lbuf_get(xb, *r1), *c1, 1) : 0;
524 *l2 = lbuf_get(xb, *r2) ? vi_insertionoffset(lbuf_get(xb, *r2), *c2, !closed) : 0;
525 if (*r1 == *r2 && lbuf_get(xb, *r1))
526 ren_region(lbuf_get(xb, *r1), *c1, *c2, l1, l2, closed);
527 if (*r1 == *r2 && *l2 < *l1)
528 swap(l1, l2);
531 static void vi_yank(int r1, int c1, int r2, int c2, int lnmode, int closed)
533 char *region;
534 int l1, l2;
535 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
536 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
537 reg_put(0, region, lnmode);
538 free(region);
539 xrow = r1;
540 xcol = lnmode ? xcol : c1;
543 static void vi_delete(int r1, int c1, int r2, int c2, int lnmode, int closed)
545 char *pref, *post;
546 char *region;
547 int l1, l2;
548 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
549 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
550 reg_put(0, region, lnmode);
551 free(region);
552 pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, l1);
553 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
554 lbuf_rm(xb, r1, r2 + 1);
555 if (!lnmode) {
556 struct sbuf *sb = sbuf_make();
557 sbuf_str(sb, pref);
558 sbuf_str(sb, post);
559 lbuf_put(xb, r1, sbuf_buf(sb));
560 sbuf_free(sb);
562 xrow = r1;
563 xcol = c1;
564 if (lnmode)
565 lbuf_postindents(xb, &xrow, &xcol);
566 free(pref);
567 free(post);
570 static int lastline(char *str)
572 char *s = str;
573 char *r = s;
574 while (s && s[0]) {
575 r = s;
576 s = strchr(s == str ? s : s + 1, '\n');
578 return r - str;
581 static int linecount(char *s)
583 int n;
584 for (n = 0; s; n++)
585 if ((s = strchr(s, '\n')))
586 s++;
587 return n;
590 static int indentscopy(char *d, char *s, int len)
592 int i;
593 for (i = 0; i < len - 1 && (s[i] == ' ' || s[i] == '\t'); i++)
594 d[i] = s[i];
595 d[i] = '\0';
596 return i;
599 static char *vi_input(char *pref, char *post, int *row, int *col)
601 char ai[64] = "";
602 char *rep;
603 struct sbuf *sb;
604 int last, off;
605 if (xautoindent)
606 pref += indentscopy(ai, pref, sizeof(ai));
607 rep = led_input(pref, post, ai, xautoindent ? sizeof(ai) - 1 : 0);
608 if (!rep)
609 return NULL;
610 sb = sbuf_make();
611 sbuf_str(sb, ai);
612 sbuf_str(sb, pref);
613 sbuf_str(sb, rep);
614 last = lastline(sbuf_buf(sb));
615 off = uc_slen(sbuf_buf(sb) + last);
616 if (last)
617 while (xautoindent && (post[0] == ' ' || post[0] == '\t'))
618 post++;
619 sbuf_str(sb, post);
620 *row = linecount(sbuf_buf(sb)) - 1;
621 *col = ren_pos(sbuf_buf(sb) + last, MAX(0, off - 1));
622 free(rep);
623 return sbuf_done(sb);
626 static char *vi_indents(char *ln)
628 struct sbuf *sb = sbuf_make();
629 while (xautoindent && ln && (*ln == ' ' || *ln == '\t'))
630 sbuf_chr(sb, *ln++);
631 return sbuf_done(sb);
634 static void vi_change(int r1, int c1, int r2, int c2, int lnmode, int closed)
636 char *region;
637 int l1, l2;
638 int row, col;
639 char *rep;
640 char *pref, *post;
641 vi_commandregion(&r1, &r2, &c1, &c2, &l1, &l2, closed);
642 region = lbuf_region(xb, r1, lnmode ? 0 : l1, r2, lnmode ? -1 : l2);
643 reg_put(0, region, lnmode);
644 free(region);
645 pref = lnmode ? vi_indents(lbuf_get(xb, r1)) : uc_sub(lbuf_get(xb, r1), 0, l1);
646 post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), l2, -1);
647 rep = vi_input(pref, post, &row, &col);
648 if (rep) {
649 lbuf_rm(xb, r1, r2 + 1);
650 lbuf_put(xb, r1, rep);
651 xrow = r1 + row - 1;
652 xcol = col;
653 free(rep);
655 free(pref);
656 free(post);
659 static void vc_motion(int cmd, int pre1)
661 int r1 = xrow, r2 = xrow; /* region rows */
662 int c1 = xcol, c2 = xcol; /* visual region columns */
663 int lnmode = 0; /* line-based region */
664 int closed = 1; /* include the last character */
665 int mv;
666 int pre2 = vi_prefix();
667 if (pre2 < 0)
668 return;
669 if ((mv = vi_motionln(&r2, cmd, pre1, pre2))) {
670 c2 = -1;
671 } else if (!(mv = vi_motion(&r2, &c2, pre1, pre2))) {
672 return;
674 if (mv < 0)
675 return;
676 if (!strchr("fFtTeE$", mv))
677 closed = 0;
678 lnmode = c2 < 0;
679 if (lnmode) {
680 lbuf_eol(xb, &r1, &c1, -1);
681 lbuf_eol(xb, &r2, &c2, +1);
683 if (cmd == 'y')
684 vi_yank(r1, c1, r2, c2, lnmode, closed);
685 if (cmd == 'd')
686 vi_delete(r1, c1, r2, c2, lnmode, closed);
687 if (cmd == 'c')
688 vi_change(r1, c1, r2, c2, lnmode, closed);
691 static void vc_insert(int cmd)
693 char *pref, *post;
694 char *ln = lbuf_get(xb, xrow);
695 int row, col, off = 0;
696 char *rep;
697 if (cmd == 'I')
698 lbuf_postindents(xb, &xrow, &xcol);
699 if (cmd == 'A') {
700 lbuf_eol(xb, &xrow, &xcol, +1);
701 lbuf_lnnext(xb, &xrow, &xcol, -1);
703 if (cmd == 'o')
704 xrow += 1;
705 if (cmd == 'i' || cmd == 'I')
706 off = ln ? vi_insertionoffset(ln, xcol, 1) : 0;
707 if (cmd == 'a' || cmd == 'A')
708 off = ln ? vi_insertionoffset(ln, xcol, 0) : 0;
709 pref = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, 0, off) : vi_indents(ln);
710 post = ln && cmd != 'o' && cmd != 'O' ? uc_sub(ln, off, -1) : uc_dup("\n");
711 rep = vi_input(pref, post, &row, &col);
712 if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
713 lbuf_put(xb, 0, "\n");
714 if (rep) {
715 if (cmd != 'o' && cmd != 'O')
716 lbuf_rm(xb, xrow, xrow + 1);
717 lbuf_put(xb, xrow, rep);
718 xrow += row - 1;
719 xcol = col;
720 free(rep);
722 free(pref);
723 free(post);
726 static void vc_put(int cmd, int cnt)
728 int lnmode;
729 char *ln;
730 char *buf = reg_get(0, &lnmode);
731 struct sbuf *sb;
732 int off;
733 int i;
734 if (!buf)
735 return;
736 ln = lnmode ? NULL : lbuf_get(xb, xrow);
737 off = ln ? vi_insertionoffset(ln, xcol, cmd == 'P') : 0;
738 if (cmd == 'p' && !ln)
739 xrow++;
740 sb = sbuf_make();
741 if (ln) {
742 char *s = uc_sub(ln, 0, off);
743 sbuf_str(sb, s);
744 free(s);
746 for (i = 0; i < MAX(cnt, 1); i++)
747 sbuf_str(sb, buf);
748 if (ln) {
749 char *s = uc_sub(ln, off, -1);
750 sbuf_str(sb, s);
751 free(s);
753 if (ln)
754 lbuf_rm(xb, xrow, xrow + 1);
755 lbuf_put(xb, xrow, sbuf_buf(sb));
756 sbuf_free(sb);
760 static int join_spaces(char *prev, char *next)
762 int prevlen = strlen(prev);
763 if (!prev[0])
764 return 0;
765 if (prev[prevlen - 1] == ' ' || next[0] == ')')
766 return 0;
767 return prev[prevlen - 1] == '.' ? 2 : 1;
770 static void vc_join(int arg)
772 struct sbuf *sb;
773 int cnt = arg <= 1 ? 2 : arg;
774 int beg = xrow;
775 int end = xrow + cnt;
776 int off = 0;
777 int i;
778 if (!lbuf_get(xb, beg) || !lbuf_get(xb, end - 1))
779 return;
780 sb = sbuf_make();
781 for (i = beg; i < end; i++) {
782 char *ln = lbuf_get(xb, i);
783 char *lnend = strchr(ln, '\n');
784 int spaces;
785 if (i > beg)
786 while (ln[0] == ' ' || ln[0] == '\t')
787 ln++;
788 spaces = i > beg ? join_spaces(sbuf_buf(sb), ln) : 0;
789 off = uc_slen(sbuf_buf(sb));
790 while (spaces--)
791 sbuf_chr(sb, ' ');
792 sbuf_mem(sb, ln, lnend - ln);
794 sbuf_chr(sb, '\n');
795 lbuf_rm(xb, beg, end);
796 lbuf_put(xb, beg, sbuf_buf(sb));
797 xcol = ren_pos(sbuf_buf(sb), off);
798 sbuf_free(sb);
801 static int vi_scrollforeward(int cnt)
803 if (xtop >= lbuf_len(xb) - 1)
804 return 1;
805 xtop = MIN(lbuf_len(xb) - 1, xtop + cnt);
806 xrow = MAX(xrow, xtop);
807 return 0;
810 static int vi_scrollbackward(int cnt)
812 if (xtop == 0)
813 return 1;
814 xtop = MAX(0, xtop - cnt);
815 xrow = MIN(xrow, xtop + xrows - 1);
816 return 0;
819 static void vi_status(void)
821 char stat[128];
822 sprintf(stat, "[%s] %d lines, %d,%d\n",
823 xpath[0] ? xpath : "unnamed", lbuf_len(xb), xrow + 1, xcol + 1);
824 led_print(stat, xrows);
827 static int vc_replace(int arg)
829 int cnt = MAX(1, arg);
830 char *cs = vi_char();
831 char *ln = lbuf_get(xb, xrow);
832 struct sbuf *sb;
833 char *pref, *post;
834 char *s;
835 int off, i;
836 if (!ln || !cs)
837 return 1;
838 off = ren_off(ln, xcol);
839 s = uc_chr(ln, off);
840 for (i = 0; s[0] != '\n' && i < cnt; i++)
841 s = uc_next(s);
842 if (i < cnt)
843 return 1;
844 pref = uc_sub(ln, 0, off);
845 post = uc_sub(ln, off + cnt, -1);
846 sb = sbuf_make();
847 sbuf_str(sb, pref);
848 for (i = 0; i < cnt; i++)
849 sbuf_str(sb, cs);
850 sbuf_str(sb, post);
851 lbuf_rm(xb, xrow, xrow + 1);
852 lbuf_put(xb, xrow, sbuf_buf(sb));
853 off += cnt - 1;
854 xcol = ren_pos(sbuf_buf(sb), off);
855 sbuf_free(sb);
856 free(pref);
857 free(post);
858 return 0;
861 static void vi(void)
863 int mark;
864 char *ln;
865 term_init();
866 xtop = 0;
867 xrow = 0;
868 lbuf_eol(xb, &xrow, &xcol, -1);
869 vi_draw();
870 term_pos(xrow, led_pos(lbuf_get(xb, xrow), xcol));
871 while (!xquit) {
872 int redraw = 0;
873 int orow = xrow;
874 int ocol = xcol;
875 int pre1, mv;
876 if ((pre1 = vi_prefix()) < 0)
877 continue;
878 mv = vi_motion(&xrow, &xcol, pre1, 0);
879 if (mv > 0) {
880 if (strchr("\'GHML/?", mv))
881 lbuf_mark(xb, '\'', orow);
882 if (xcol < 0) {
883 if (strchr("jk", mv))
884 xcol = ocol;
885 else
886 lbuf_postindents(xb, &xrow, &xcol);
888 } else if (mv == 0) {
889 int c = vi_read();
890 int z;
891 if (c <= 0)
892 continue;
893 switch (c) {
894 case TK_CTL('b'):
895 if (vi_scrollbackward((pre1 ? pre1 : 1) * (xrows - 1)))
896 break;
897 lbuf_postindents(xb, &xrow, &xcol);
898 redraw = 1;
899 break;
900 case TK_CTL('f'):
901 if (vi_scrollforeward((pre1 ? pre1 : 1) * (xrows - 1)))
902 break;
903 lbuf_postindents(xb, &xrow, &xcol);
904 redraw = 1;
905 break;
906 case TK_CTL('e'):
907 if (vi_scrollforeward((pre1 ? pre1 : 1)))
908 break;
909 redraw = 1;
910 break;
911 case TK_CTL('y'):
912 if (vi_scrollbackward((pre1 ? pre1 : 1)))
913 break;
914 redraw = 1;
915 break;
916 case 'u':
917 lbuf_undo(xb);
918 redraw = 1;
919 break;
920 case TK_CTL('r'):
921 lbuf_redo(xb);
922 redraw = 1;
923 break;
924 case TK_CTL('g'):
925 vi_status();
926 break;
927 case TK_CTL('^'):
928 ex_command("e #");
929 redraw = 1;
930 break;
931 case ':':
932 term_pos(xrows, led_pos(":", 0));
933 term_kill();
934 ln = led_prompt(":", "");
935 if (ln && ln[0]) {
936 ex_command(ln);
937 redraw = 1;
939 free(ln);
940 if (xquit)
941 continue;
942 break;
943 case 'c':
944 case 'd':
945 case 'y':
946 vc_motion(c, pre1);
947 redraw = 1;
948 break;
949 case 'i':
950 case 'I':
951 case 'a':
952 case 'A':
953 case 'o':
954 case 'O':
955 vc_insert(c);
956 redraw = 1;
957 break;
958 case 'J':
959 vc_join(pre1);
960 redraw = 1;
961 break;
962 case 'm':
963 if ((mark = vi_read()) > 0 && isalpha(mark))
964 lbuf_mark(xb, mark, xrow);
965 break;
966 case 'p':
967 case 'P':
968 vc_put(c, pre1);
969 redraw = 1;
970 break;
971 case 'z':
972 z = vi_read();
973 switch (z) {
974 case '\n':
975 xtop = pre1 ? pre1 : xrow;
976 break;
977 case '.':
978 xtop = MAX(0, (pre1 ? pre1 : xrow) - xrows / 2);
979 break;
980 case '-':
981 xtop = MAX(0, (pre1 ? pre1 : xrow) - xrows + 1);
982 break;
983 case 'l':
984 case 'r':
985 case 'L':
986 case 'R':
987 xdir = z;
988 break;
990 redraw = 1;
991 break;
992 case 'x':
993 vi_back(' ');
994 vc_motion('d', pre1);
995 redraw = 1;
996 break;
997 case 'X':
998 vi_back(TK_CTL('h'));
999 vc_motion('d', pre1);
1000 redraw = 1;
1001 break;
1002 case 'C':
1003 vi_back('$');
1004 vc_motion('c', pre1);
1005 redraw = 1;
1006 break;
1007 case 'D':
1008 vi_back('$');
1009 vc_motion('d', pre1);
1010 redraw = 1;
1011 break;
1012 case 'r':
1013 vc_replace(pre1);
1014 redraw = 1;
1015 break;
1016 case 's':
1017 vi_back(' ');
1018 vc_motion('c', pre1);
1019 redraw = 1;
1020 break;
1021 case 'S':
1022 vi_back('c');
1023 vc_motion('c', pre1);
1024 redraw = 1;
1025 break;
1026 case 'Y':
1027 vi_back('y');
1028 vc_motion('y', pre1);
1029 redraw = 1;
1030 break;
1031 default:
1032 continue;
1035 if (xrow < 0 || xrow >= lbuf_len(xb))
1036 xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
1037 if (xrow < xtop || xrow >= xtop + xrows) {
1038 xtop = xrow < xtop ? xrow : MAX(0, xrow - xrows + 1);
1039 redraw = 1;
1041 if (redraw)
1042 vi_draw();
1043 term_pos(xrow - xtop, led_pos(lbuf_get(xb, xrow),
1044 ren_cursor(lbuf_get(xb, xrow), xcol)));
1045 lbuf_undomark(xb);
1047 term_pos(xrows, 0);
1048 term_kill();
1049 term_done();
1052 int main(int argc, char *argv[])
1054 char ecmd[PATHLEN];
1055 int i;
1056 xb = lbuf_make();
1057 xvis = 1;
1058 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
1059 if (argv[i][1] == 's')
1060 xled = 0;
1061 if (argv[i][1] == 'e')
1062 xvis = 0;
1063 if (argv[i][1] == 'v')
1064 xvis = 1;
1066 dir_init();
1067 if (i < argc) {
1068 snprintf(ecmd, PATHLEN, "e %s", argv[i]);
1069 ex_command(ecmd);
1071 if (xvis)
1072 vi();
1073 else
1074 ex();
1075 lbuf_free(xb);
1076 reg_done();
1077 dir_done();
1078 return 0;