4 * Copyright (C) 2015 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
15 char xpath
[PATHLEN
]; /* current file */
16 struct lbuf
*xb
; /* current buffer */
17 int xrow
, xcol
, xtop
; /* current row, column, and top row */
18 int xled
= 1; /* use the line editor */
19 int xdir
= 'L'; /* current direction context */
22 static void vi_draw(void)
26 for (i
= xtop
; i
< xtop
+ xrows
; i
++) {
27 char *s
= lbuf_get(xb
, i
);
28 led_print(s
? s
: "~", i
- xtop
);
34 static int vi_buf
[128];
37 static int vi_read(void)
39 return vi_buflen
? vi_buf
[--vi_buflen
] : term_read(1000);
42 static void vi_back(int c
)
44 if (vi_buflen
< sizeof(vi_buf
))
45 vi_buf
[vi_buflen
++] = c
;
48 static char *vi_char(void)
50 return led_keymap(vi_read());
53 static int vi_prefix(void)
57 if ((c
>= '1' && c
<= '9')) {
59 pre
= pre
* 10 + c
- '0';
67 static int lbuf_lnnext(struct lbuf
*lb
, int *r
, int *c
, int dir
)
69 char *ln
= lbuf_get(lb
, *r
);
70 int col
= ln
? ren_next(ln
, *c
, dir
) : -1;
77 static void lbuf_eol(struct lbuf
*lb
, int *r
, int *c
, int dir
)
79 char *ln
= lbuf_get(lb
, *r
);
80 *c
= ren_eol(ln
? ln
: "", dir
);
83 static int lbuf_next(struct lbuf
*lb
, int *r
, int *c
, int dir
)
85 if (dir
< 0 && *r
>= lbuf_len(lb
))
86 *r
= MAX(0, lbuf_len(lb
) - 1);
87 if (lbuf_lnnext(lb
, r
, c
, dir
)) {
88 if (!lbuf_get(lb
, *r
+ dir
))
91 lbuf_eol(lb
, r
, c
, -dir
);
97 /* return a static buffer to the character at visual position c of line r */
98 static char *lbuf_chr(struct lbuf
*lb
, int r
, int c
)
101 char *ln
= lbuf_get(lb
, r
);
103 int off
= ren_off(ln
, c
);
104 char *s
= uc_chr(ln
, off
);
106 memcpy(chr
, s
, uc_len(s
));
107 chr
[uc_len(s
)] = '\0';
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))
122 static void lbuf_findchar(struct lbuf
*lb
, int *row
, int *col
, char *cs
, int dir
, int n
)
127 while (n
> 0 && !lbuf_lnnext(lb
, row
, &c
, dir
))
128 if (uc_code(lbuf_chr(lb
, *row
, c
)) == uc_code(cs
))
134 static void lbuf_tochar(struct lbuf
*lb
, int *row
, int *col
, char *cs
, int dir
, int n
)
139 while (n
> 0 && !lbuf_lnnext(lb
, row
, &c
, dir
))
140 if (uc_code(lbuf_chr(lb
, *row
, c
)) == uc_code(cs
))
144 lbuf_lnnext(lb
, row
, col
, -dir
);
148 static int vi_motionln(int *row
, int cmd
, int pre1
, int pre2
)
150 int pre
= (pre1
? pre1
: 1) * (pre2
? pre2
: 1);
156 *row
= MIN(*row
+ pre
, lbuf_len(xb
) - 1);
159 *row
= MAX(*row
- pre
, 0);
162 *row
= MIN(*row
+ pre
- 1, lbuf_len(xb
) - 1);
165 if ((mark
= vi_read()) > 0 && (isalpha(mark
) || mark
== '\''))
166 if (lbuf_markpos(xb
, mark
) >= 0)
167 *row
= lbuf_markpos(xb
, mark
);
170 *row
= MIN(*row
+ pre
, lbuf_len(xb
) - 1);
173 *row
= MAX(*row
- pre
, 0);
176 *row
= (pre1
|| pre2
) ? pre
- 1 : lbuf_len(xb
) - 1;
180 *row
= MIN(xtop
+ pre
- 1, lbuf_len(xb
) - 1);
186 *row
= MIN(xtop
+ xrows
- 1 - pre
+ 1, lbuf_len(xb
) - 1);
192 *row
= MIN(xtop
+ xrows
/ 2, lbuf_len(xb
) - 1);
198 *row
= MIN(*row
+ pre
- 1, lbuf_len(xb
) - 1);
207 /* move to the last character of the word */
208 static int lbuf_wordlast(struct lbuf
*lb
, int *row
, int *col
, int kind
, int dir
)
210 if (!kind
|| !(uc_kind(lbuf_chr(lb
, *row
, *col
)) & kind
))
212 while (uc_kind(lbuf_chr(lb
, *row
, *col
)) & kind
)
213 if (lbuf_next(lb
, row
, col
, dir
))
215 if (!(uc_kind(lbuf_chr(lb
, *row
, *col
)) & kind
))
216 lbuf_next(lb
, row
, col
, -dir
);
220 static int lbuf_wordbeg(struct lbuf
*lb
, int *row
, int *col
, int big
, int dir
)
223 lbuf_wordlast(lb
, row
, col
, big
? 3 : uc_kind(lbuf_chr(lb
, *row
, *col
)), dir
);
224 if (lbuf_next(lb
, row
, col
, dir
))
226 while (uc_isspace(lbuf_chr(lb
, *row
, *col
))) {
227 nl
= uc_code(lbuf_chr(lb
, *row
, *col
)) == '\n' ? nl
+ 1 : 0;
230 if (lbuf_next(lb
, row
, col
, dir
))
236 static int lbuf_wordend(struct lbuf
*lb
, int *row
, int *col
, int big
, int dir
)
238 int nl
= uc_code(lbuf_chr(lb
, *row
, *col
)) == '\n' ? -1 : 0;
239 if (!uc_isspace(lbuf_chr(lb
, *row
, *col
)))
240 if (lbuf_next(lb
, row
, col
, dir
))
242 while (uc_isspace(lbuf_chr(lb
, *row
, *col
))) {
243 nl
= uc_code(lbuf_chr(lb
, *row
, *col
)) == '\n' ? nl
+ 1 : 0;
246 lbuf_next(lb
, row
, col
, -dir
);
249 if (lbuf_next(lb
, row
, col
, dir
))
252 if (lbuf_wordlast(lb
, row
, col
, big
? 3 : uc_kind(lbuf_chr(lb
, *row
, *col
)), dir
))
257 static int vi_motion(int *row
, int *col
, int pre1
, int pre2
)
260 int pre
= (pre1
? pre1
: 1) * (pre2
? pre2
: 1);
261 char *ln
= lbuf_get(xb
, *row
);
262 int dir
= ren_dir(ln
? ln
: "");
266 for (i
= 0; i
< pre
; i
++)
267 if (lbuf_next(xb
, row
, col
, 1))
271 lbuf_findchar(xb
, row
, col
, vi_char(), +1, pre
);
274 lbuf_findchar(xb
, row
, col
, vi_char(), -1, pre
);
277 for (i
= 0; i
< pre
; i
++)
278 if (lbuf_lnnext(xb
, row
, col
, -1 * dir
))
282 for (i
= 0; i
< pre
; i
++)
283 if (lbuf_lnnext(xb
, row
, col
, +1 * dir
))
287 lbuf_tochar(xb
, row
, col
, vi_char(), 1, pre
);
290 lbuf_tochar(xb
, row
, col
, vi_char(), 0, pre
);
293 for (i
= 0; i
< pre
; i
++)
294 if (lbuf_wordend(xb
, row
, col
, 1, -1))
298 for (i
= 0; i
< pre
; i
++)
299 if (lbuf_wordend(xb
, row
, col
, 1, +1))
303 for (i
= 0; i
< pre
; i
++)
304 if (lbuf_wordbeg(xb
, row
, col
, 1, +1))
308 for (i
= 0; i
< pre
; i
++)
309 if (lbuf_wordend(xb
, row
, col
, 0, -1))
313 for (i
= 0; i
< pre
; i
++)
314 if (lbuf_wordend(xb
, row
, col
, 0, +1))
318 for (i
= 0; i
< pre
; i
++)
319 if (lbuf_wordbeg(xb
, row
, col
, 0, +1))
323 lbuf_eol(xb
, row
, col
, -1);
326 lbuf_postindents(xb
, row
, col
);
329 lbuf_eol(xb
, row
, col
, +1);
330 lbuf_lnnext(xb
, row
, col
, -1);
337 *col
= ren_cursor(ln
, *col
);
338 for (i
= 0; i
< pre
; i
++)
339 if (lbuf_next(xb
, row
, col
, -1))
349 static void swap(int *a
, int *b
)
356 static char *lbuf_region(struct lbuf
*lb
, int r1
, int l1
, int r2
, int l2
)
361 return uc_sub(lbuf_get(lb
, r1
), l1
, l2
);
363 s1
= uc_sub(lbuf_get(lb
, r1
), l1
, -1);
364 s3
= uc_sub(lbuf_get(lb
, r2
), 0, l2
);
365 s2
= lbuf_cp(lb
, r1
+ 1, r2
);
372 return sbuf_done(sb
);
375 static void vc_motion(int c
, int pre1
)
377 int r1
= xrow
, r2
= xrow
; /* region rows */
378 int c1
= xcol
, c2
= xcol
; /* visual region columns */
379 int l1
, l2
; /* logical region columns */
380 int ln
= 0; /* line-based region */
381 int closed
= 1; /* include the last character */
385 int pre2
= vi_prefix();
388 if (vi_motionln(&r2
, c
, pre1
, pre2
)) {
390 lbuf_eol(xb
, &r1
, &c1
, -1);
391 lbuf_eol(xb
, &r2
, &c2
, +1);
392 } else if ((mv
= vi_motion(&r2
, &c2
, pre1
, pre2
))) {
393 if (!strchr("fFtTeE$", mv
))
398 /* make sure the first position is visually before the second */
399 if (r2
< r1
|| (r2
== r1
&& ren_cmp(lbuf_get(xb
, r1
), c1
, c2
) > 0)) {
403 if (c
== 'y') { /* adjusting cursor position */
405 xcol
= ln
? xcol
: c1
;
407 for (i
= 0; i
< 2; i
++) {
408 l1
= ren_insertionoffset(lbuf_get(xb
, r1
), c1
, 1);
409 l2
= ren_insertionoffset(lbuf_get(xb
, r2
), c2
, !closed
);
410 if (r1
== r2
&& l2
< l1
) /* offsets out of order */
413 pref
= ln
? uc_dup("") : uc_sub(lbuf_get(xb
, r1
), 0, l1
);
414 post
= ln
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), l2
, -1);
415 if (c
== 'c' || c
== 'd' || c
== 'y') {
416 char *region
= lbuf_region(xb
, r1
, ln
? 0 : l1
, r2
, ln
? -1 : l2
);
417 reg_put(0, region
, ln
);
422 char *rep
= led_input(pref
, post
, &row
, &col
);
424 lbuf_rm(xb
, r1
, r2
+ 1);
425 lbuf_put(xb
, r1
, rep
);
432 lbuf_rm(xb
, r1
, r2
+ 1);
434 struct sbuf
*sb
= sbuf_make();
437 lbuf_put(xb
, r1
, sbuf_buf(sb
));
443 lbuf_postindents(xb
, &xrow
, &xcol
);
449 static void vc_insert(int cmd
)
452 char *ln
= lbuf_get(xb
, xrow
);
453 int row
, col
, off
= 0;
456 lbuf_postindents(xb
, &xrow
, &xcol
);
458 lbuf_eol(xb
, &xrow
, &xcol
, +1);
459 lbuf_lnnext(xb
, &xrow
, &xcol
, -1);
463 if (cmd
== 'o' || cmd
== 'O')
465 if (cmd
== 'i' || cmd
== 'I')
466 off
= ln
? ren_insertionoffset(ln
, xcol
, 1) : 0;
467 if (cmd
== 'a' || cmd
== 'A')
468 off
= ln
? ren_insertionoffset(ln
, xcol
, 0) : 0;
469 pref
= ln
? uc_sub(ln
, 0, off
) : uc_dup("");
470 post
= ln
? uc_sub(ln
, off
, -1) : uc_dup("\n");
471 rep
= led_input(pref
, post
, &row
, &col
);
473 if (cmd
!= 'o' && cmd
!= 'O')
474 lbuf_rm(xb
, xrow
, xrow
+ 1);
475 lbuf_put(xb
, xrow
, rep
);
484 static void vc_put(int cmd
, int cnt
)
488 char *buf
= reg_get(0, &lnmode
);
494 ln
= lnmode
? NULL
: lbuf_get(xb
, xrow
);
495 off
= ln
? ren_insertionoffset(ln
, xcol
, cmd
== 'P') : 0;
496 if (cmd
== 'p' && !ln
)
500 char *s
= uc_sub(ln
, 0, off
);
504 for (i
= 0; i
< MAX(cnt
, 1); i
++)
507 char *s
= uc_sub(ln
, off
, -1);
512 lbuf_rm(xb
, xrow
, xrow
+ 1);
513 lbuf_put(xb
, xrow
, sbuf_buf(sb
));
524 lbuf_eol(xb
, &xrow
, &xcol
, -1);
526 term_pos(xrow
, xcol
);
531 if ((pre1
= vi_prefix()) < 0)
533 if ((mv
= vi_motionln(&xrow
, 0, pre1
, 0))) {
534 if (strchr("\'GHML", mv
))
535 lbuf_mark(xb
, '\'', orow
);
536 if (!strchr("jk", mv
))
537 lbuf_postindents(xb
, &xrow
, &xcol
);
538 } else if (!vi_motion(&xrow
, &xcol
, pre1
, 0)) {
549 xtop
= MAX(0, xtop
- xrows
+ 1);
550 xrow
= xtop
+ xrows
- 1;
551 lbuf_postindents(xb
, &xrow
, &xcol
);
556 xtop
= MIN(lbuf_len(xb
) - 1, xtop
+ xrows
- 1);
560 lbuf_postindents(xb
, &xrow
, &xcol
);
591 if ((mark
= vi_read()) > 0 && isalpha(mark
))
592 lbuf_mark(xb
, mark
, xrow
);
603 xtop
= pre1
? pre1
: xrow
;
606 xtop
= MAX(0, (pre1
? pre1
: xrow
) - xrows
/ 2);
609 xtop
= MAX(0, (pre1
? pre1
: xrow
) - xrows
+ 1);
624 if (xrow
< 0 || xrow
>= lbuf_len(xb
))
625 xrow
= lbuf_len(xb
) ? lbuf_len(xb
) - 1 : 0;
626 if (xrow
< xtop
|| xrow
>= xtop
+ xrows
) {
627 xtop
= xrow
< xtop
? xrow
: MAX(0, xrow
- xrows
+ 1);
632 term_pos(xrow
- xtop
, ren_cursor(lbuf_get(xb
, xrow
), xcol
));
640 int main(int argc
, char *argv
[])
646 for (i
= 1; i
< argc
&& argv
[i
][0] == '-'; i
++) {
647 if (argv
[i
][1] == 's')
649 if (argv
[i
][1] == 'e')
651 if (argv
[i
][1] == 'v')
655 snprintf(ecmd
, PATHLEN
, "e %s", argv
[i
]);