4 * Copyright (C) 2015-2023 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 static char vi_msg
[EXLEN
]; /* current message */
28 static char vi_charlast
[8]; /* the last character searched via f, t, F, or T */
29 static int vi_charcmd
; /* the character finding command */
30 static int vi_arg1
, vi_arg2
; /* the first and second arguments */
31 static int vi_ybuf
; /* current yank buffer */
32 static int vi_pcol
; /* the column requested by | command */
33 static int vi_printed
; /* ex_print() calls since the last command */
34 static int vi_scroll
; /* scroll amount for ^f and ^d*/
35 static int vi_soset
, vi_so
; /* search offset; 1 in "/kw/1" */
36 static int w_cnt
= 1; /* window count */
37 static int w_cur
; /* active window identifier */
38 static int w_tmp
; /* temporary window */
39 static char *w_path
; /* saved window path */
40 static int w_row
, w_off
, w_top
, w_left
; /* saved window configuration */
42 static void vc_status(void);
44 static void vi_wait(void)
46 if (vi_printed
> 1 || vi_printed
< 0) {
48 term_window(0, term_rowx() - 1);
50 free(led_prompt("[enter to continue]", "", &xkmap
, xhl
? "---" : "___", NULL
));
56 static void vi_drawmsg(void)
60 led_printmsg(vi_msg
[0] ? vi_msg
: "\n", xrows
, xhl
? "---" : "___");
65 static void vi_drawrow(int row
)
67 char *s
= lbuf_get(xb
, row
);
68 if (xhll
&& row
== xrow
)
69 syn_context(conf_hlline());
70 led_print(s
? s
: (row
? "~" : ""), row
- xtop
, xhl
? ex_filetype() : "");
74 /* redraw the screen */
75 static void vi_drawagain(int xcol
, int lineonly
)
79 for (i
= xtop
; i
< xtop
+ xrows
; i
++)
80 if (!lineonly
|| i
== xrow
)
83 term_pos(xrow
, led_pos(lbuf_get(xb
, i
), xcol
));
87 /* update the screen */
88 static void vi_drawupdate(int xcol
, int otop
)
94 term_room(otop
- xtop
);
96 int n
= MIN(xtop
- otop
, xrows
);
97 for (i
= 0; i
< n
; i
++)
98 vi_drawrow(xtop
+ xrows
- n
+ i
);
100 int n
= MIN(otop
- xtop
, xrows
);
101 for (i
= 0; i
< n
; i
++)
102 vi_drawrow(xtop
+ i
);
104 term_pos(xrow
, led_pos(lbuf_get(xb
, i
), xcol
));
108 term_pos(xrow
, led_pos(lbuf_get(xb
, i
), xcol
));
111 /* update the screen by removing lines r1 to r2 before an input command */
112 static void vi_drawrm(int r1
, int r2
, int newln
)
114 r1
= MIN(MAX(r1
, xtop
), xtop
+ xrows
- 1);
115 r2
= MIN(MAX(r2
, xtop
), xtop
+ xrows
- 1);
116 term_pos(r1
- xtop
, 0);
117 term_room(r1
- r2
+ newln
);
120 static int vi_switch(int id
)
123 int cnt
= term_rowx();
128 char *old
= w_path
&& w_path
[0] ? w_path
: "/";
129 int row
= w_row
, off
= w_off
, top
= w_top
, left
= w_left
;
131 if (w_path
&& strcmp(w_path
, ex_path()) == 0)
133 snprintf(cmd
, sizeof(cmd
), "%s! %s", ec
, old
);
135 w_path
= uc_dup(ex_path());
136 w_row
= xrow
, w_off
= xoff
, w_top
= xtop
, w_left
= xleft
;
138 xrow
= row
, xoff
= off
, xtop
= top
, xleft
= left
;
142 beg
= id
== 0 ? 0 : half
;
143 cnt
= id
== 0 ? half
: term_rowx() - half
;
145 term_window(beg
, cnt
- 1);
150 static int vi_wsplit(void)
156 w_path
= uc_dup(ex_path());
157 w_row
= xrow
, w_off
= xoff
, w_top
= xtop
, w_left
= xleft
;
161 static int vi_wonly(void)
170 static int vi_wclose(void)
174 vi_switch(1 - w_cur
);
178 static int vi_wswap(void)
186 static void vi_wfix(void)
188 if (xrow
< 0 || xrow
>= lbuf_len(xb
))
189 xrow
= lbuf_len(xb
) ? lbuf_len(xb
) - 1 : 0;
191 xtop
= xtop
- xrows
/ 2 > xrow
?
192 MAX(0, xrow
- xrows
/ 2) : xrow
;
193 if (xtop
+ xrows
<= xrow
)
194 xtop
= xtop
+ xrows
+ xrows
/ 2 <= xrow
?
195 xrow
- xrows
/ 2 : xrow
- xrows
+ 1;
196 xoff
= ren_noeol(lbuf_get(xb
, xrow
), xoff
);
199 static int vi_buf
[128];
200 static int vi_buflen
;
202 static int vi_read(void)
204 return vi_buflen
? vi_buf
[--vi_buflen
] : term_read();
207 static void vi_back(int c
)
209 if (vi_buflen
< sizeof(vi_buf
))
210 vi_buf
[vi_buflen
++] = c
;
213 static char *vi_char(void)
215 return led_read(&xkmap
);
218 static char *vi_prompt(char *msg
, int *kmap
, char *hist
)
221 term_pos(xrows
, led_pos(msg
, 0));
223 s
= led_prompt(msg
, "", kmap
, xhl
? "---" : "___", xhist
!= 0 ? hist
: NULL
);
226 r
= uc_dup(strlen(s
) >= strlen(msg
) ? s
+ strlen(msg
) : s
);
231 /* read an ex input line */
232 char *ex_read(char *msg
)
237 term_pos(xrows
- 1, 0);
239 char *s
= led_prompt(msg
, "", &xkmap
, xhl
? "---" : "___", NULL
);
245 while ((c
= getchar()) != EOF
&& c
!= '\n')
251 return sbuf_done(sb
);
254 /* show an ex message */
255 void ex_show(char *msg
)
258 snprintf(vi_msg
, sizeof(vi_msg
), "%s", msg
);
260 led_print(msg
, -1, xhl
? "---" : "___");
267 /* print an ex output line */
268 void ex_print(char *line
)
271 if (line
&& vi_printed
== 0)
272 snprintf(vi_msg
, sizeof(vi_msg
), "%s", line
);
273 if (line
&& vi_printed
== 1)
274 led_print(vi_msg
, xrows
- 1, xhl
? "-ex" : "");
277 if (line
&& vi_printed
)
278 led_print(line
, xrows
- 1, xhl
? "-ex" : "");
279 vi_printed
+= line
!= NULL
? 1 : -1000;
286 static char *reg_getln(int h
)
288 return reg_get(0x80 | h
, NULL
);
291 static void reg_putln(int h
, char *s
)
293 char *old
= reg_get(0x80 | h
, NULL
);
296 if (xhist
== 0 || s
== NULL
|| s
[0] == '\0' || s
[0] == '\n')
298 /* ignore duplicate lines */
300 while (s
[i
] && old
[i
] == s
[i
])
302 if (!s
[i
] && old
[i
] == '\n')
306 if (xhist
> 0 && old
!= NULL
) {
308 for (i
= 1; end
!= NULL
&& i
< xhist
; i
++)
309 end
= strchr(end
== old
? end
: end
+ 1, '\n');
313 /* add the new line */
316 if (strchr(s
, '\n') == NULL
)
320 reg_put(0x80 | h
, sbuf_buf(sb
), 1);
324 static int vi_yankbuf(void)
328 return (c
= vi_read()) == '\\' ? 0x80 | vi_read() : c
;
333 static int vi_prefix(void)
337 if ((c
>= '1' && c
<= '9')) {
339 n
= n
* 10 + c
- '0';
347 static int vi_col2off(struct lbuf
*lb
, int row
, int col
)
349 char *ln
= lbuf_get(lb
, row
);
350 return ln
? ren_off(ln
, col
) : 0;
353 static int vi_off2col(struct lbuf
*lb
, int row
, int off
)
355 char *ln
= lbuf_get(lb
, row
);
356 return ln
? ren_pos(ln
, off
) : 0;
359 static int vi_nextoff(struct lbuf
*lb
, int dir
, int *row
, int *off
)
362 if (o
< 0 || !lbuf_get(lb
, *row
) || o
>= uc_slen(lbuf_get(lb
, *row
)))
368 static int vi_nextcol(struct lbuf
*lb
, int dir
, int *row
, int *off
)
370 char *ln
= lbuf_get(lb
, *row
);
371 int col
= ln
? ren_pos(ln
, *off
) : 0;
372 int o
= ln
? ren_next(ln
, col
, dir
) : -1;
375 *off
= ren_off(ln
, o
);
379 static int vi_findchar(struct lbuf
*lb
, char *cs
, int cmd
, int n
, int *row
, int *off
)
381 if (cs
!= vi_charlast
)
382 strcpy(vi_charlast
, cs
);
384 return lbuf_findchar(lb
, cs
, cmd
, n
, row
, off
);
387 static int vi_search(int cmd
, int cnt
, int *row
, int *off
)
395 if (cmd
== '/' || cmd
== '?') {
396 char sign
[4] = {cmd
};
398 char *kw
= vi_prompt(sign
, &xkmap
, reg_getln('/'));
407 if ((re
= re_read(&kw
))) {
408 ex_kwdset(re
[0] ? re
: NULL
, cmd
== '/' ? +1 : -1);
421 if (!lbuf_len(xb
) || ex_kwd(&kwd
, &dir
))
423 dir
= cmd
== 'N' ? -dir
: dir
;
425 for (i
= 0; i
< cnt
; i
++) {
426 if (lbuf_search(xb
, kwd
, dir
, &r
, &o
, &len
)) {
427 failed
= " not found";
430 if (i
+ 1 < cnt
&& cmd
== '/')
438 if (*row
+ vi_so
< 0 || *row
+ vi_so
>= lbuf_len(xb
))
439 failed
= " bad offset";
445 snprintf(vi_msg
, sizeof(vi_msg
), "/%s/%s", kwd
, failed
? failed
: "");
446 return failed
!= NULL
;
449 /* read a line motion */
450 static int vi_motionln(int *row
, int cmd
)
452 int cnt
= (vi_arg1
? vi_arg1
: 1) * (vi_arg2
? vi_arg2
: 1);
454 int mark
, mark_row
, mark_off
;
458 *row
= MIN(*row
+ cnt
, lbuf_len(xb
) - 1);
461 *row
= MAX(*row
- cnt
, 0);
464 *row
= MIN(*row
+ cnt
- 1, lbuf_len(xb
) - 1);
467 if ((mark
= vi_read()) <= 0)
469 if (lbuf_jump(xb
, mark
, &mark_row
, &mark_off
))
474 *row
= MIN(*row
+ cnt
, lbuf_len(xb
) - 1);
477 *row
= MAX(*row
- cnt
, 0);
480 *row
= (vi_arg1
|| vi_arg2
) ? cnt
- 1 : lbuf_len(xb
) - 1;
483 *row
= MIN(xtop
+ cnt
- 1, lbuf_len(xb
) - 1);
486 *row
= MIN(xtop
+ xrows
- 1 - cnt
+ 1, lbuf_len(xb
) - 1);
489 *row
= MIN(xtop
+ xrows
/ 2, lbuf_len(xb
) - 1);
493 *row
= MIN(*row
+ cnt
- 1, lbuf_len(xb
) - 1);
496 if (c
== '%' && (vi_arg1
|| vi_arg2
)) {
499 *row
= MAX(0, lbuf_len(xb
) - 1) * cnt
/ 100;
510 static int vi_curword(struct lbuf
*lb
, char *dst
, int len
, int row
, int off
, char *ext
)
512 char *ln
= lbuf_get(lb
, row
);
516 beg
= uc_chr(ln
, ren_noeol(ln
, off
));
518 while (*end
&& (uc_kind(end
) == 1 ||
519 strchr(ext
, (unsigned char) end
[0]) != NULL
))
521 while (beg
> ln
&& (uc_kind(uc_beg(ln
, beg
- 1)) == 1 ||
522 strchr(ext
, (unsigned char) beg
[-1]) != NULL
))
523 beg
= uc_beg(ln
, beg
- 1);
526 len
= len
- 1 < end
- beg
? len
- 1 : end
- beg
;
528 memcpy(dst
, beg
, len
);
533 static int vi_motion(int *row
, int *off
)
535 char cw
[120], kw
[128];
536 int cnt
= (vi_arg1
? vi_arg1
: 1) * (vi_arg2
? vi_arg2
: 1);
537 char *ln
= lbuf_get(xb
, *row
);
538 int dir
= dir_context(ln
? ln
: "");
539 int mark
, mark_row
, mark_off
;
543 if ((mv
= vi_motionln(row
, 0))) {
550 if (!(cs
= vi_char()))
552 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
556 if (!(cs
= vi_char()))
558 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
564 if (vi_findchar(xb
, vi_charlast
, vi_charcmd
, cnt
, row
, off
))
570 if (vi_findchar(xb
, vi_charlast
, vi_charcmd
, -cnt
, row
, off
))
574 for (i
= 0; i
< cnt
; i
++)
575 if (vi_nextcol(xb
, -1 * dir
, row
, off
))
579 for (i
= 0; i
< cnt
; i
++)
580 if (vi_nextcol(xb
, +1 * dir
, row
, off
))
584 if (!(cs
= vi_char()))
586 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
590 if (!(cs
= vi_char()))
592 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
596 for (i
= 0; i
< cnt
; i
++)
597 if (lbuf_wordend(xb
, 1, -1, row
, off
))
601 for (i
= 0; i
< cnt
; i
++)
602 if (lbuf_wordend(xb
, 1, +1, row
, off
))
606 for (i
= 0; i
< cnt
; i
++)
607 if (lbuf_wordbeg(xb
, 1, +1, row
, off
))
611 for (i
= 0; i
< cnt
; i
++)
612 if (lbuf_wordend(xb
, 0, -1, row
, off
))
616 for (i
= 0; i
< cnt
; i
++)
617 if (lbuf_wordend(xb
, 0, +1, row
, off
))
621 for (i
= 0; i
< cnt
; i
++)
622 if (lbuf_wordbeg(xb
, 0, +1, row
, off
))
626 for (i
= 0; i
< cnt
; i
++)
627 if (lbuf_paragraphbeg(xb
, -1, row
, off
))
631 for (i
= 0; i
< cnt
; i
++)
632 if (lbuf_paragraphbeg(xb
, +1, row
, off
))
636 if (vi_read() != '[')
638 for (i
= 0; i
< cnt
; i
++)
639 if (lbuf_sectionbeg(xb
, -1, conf_section(ex_filetype()), row
, off
))
643 if (vi_read() != ']')
645 for (i
= 0; i
< cnt
; i
++)
646 if (lbuf_sectionbeg(xb
, +1, conf_section(ex_filetype()), row
, off
))
653 *off
= lbuf_indents(xb
, *row
);
656 *off
= lbuf_eol(xb
, *row
);
659 *off
= vi_col2off(xb
, *row
, cnt
- 1);
663 if (vi_search(mv
, cnt
, row
, off
))
667 if (vi_search(mv
, cnt
, row
, off
))
671 if (vi_search(mv
, cnt
, row
, off
))
675 if (vi_search(mv
, cnt
, row
, off
))
679 if (vi_curword(xb
, cw
, sizeof(cw
), *row
, *off
, "") != 0)
681 snprintf(kw
, sizeof(kw
), "\\<%s\\>", cw
);
684 if (vi_search('n', cnt
, row
, off
))
688 for (i
= 0; i
< cnt
; i
++)
689 if (vi_nextoff(xb
, +1, row
, off
))
694 for (i
= 0; i
< cnt
; i
++)
695 if (vi_nextoff(xb
, -1, row
, off
))
699 if ((mark
= vi_read()) <= 0)
701 if (lbuf_jump(xb
, mark
, &mark_row
, &mark_off
))
707 if (lbuf_pair(xb
, row
, off
))
717 static void swap(int *a
, int *b
)
724 static char *lbuf_region(struct lbuf
*lb
, int r1
, int o1
, int r2
, int o2
)
729 return uc_sub(lbuf_get(lb
, r1
), o1
, o2
);
731 s1
= uc_sub(lbuf_get(lb
, r1
), o1
, -1);
732 s3
= uc_sub(lbuf_get(lb
, r2
), 0, o2
);
733 s2
= lbuf_cp(lb
, r1
+ 1, r2
);
740 return sbuf_done(sb
);
743 static void vi_yank(int r1
, int o1
, int r2
, int o2
, int lnmode
)
746 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
747 reg_put(vi_ybuf
, region
, lnmode
);
750 xoff
= lnmode
? xoff
: o1
;
753 static void vi_delete(int r1
, int o1
, int r2
, int o2
, int lnmode
)
757 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
758 reg_put(vi_ybuf
, region
, lnmode
);
760 pref
= lnmode
? uc_dup("") : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
761 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
763 char *line
= uc_cat(pref
, post
);
764 lbuf_edit(xb
, line
, r1
, r2
+ 1);
767 lbuf_edit(xb
, NULL
, r1
, r2
+ 1);
770 xoff
= lnmode
? lbuf_indents(xb
, xrow
) : o1
;
775 static int linecount(char *s
)
779 if ((s
= strchr(s
, '\n')))
784 static int charcount(char *text
, char *post
)
786 int tlen
= strlen(text
);
787 int plen
= strlen(post
);
792 for (i
= 0; i
< tlen
- plen
; i
++)
795 return uc_slen(nl
) - uc_slen(post
);
798 static char *vi_input(char *pref
, char *post
, int *row
, int *off
)
800 char *rep
= led_input(pref
, post
, &xkmap
, xhl
? ex_filetype() : "");
803 *row
= linecount(rep
) - 1;
804 *off
= charcount(rep
, post
) - 1;
810 static char *vi_indents(char *ln
)
812 struct sbuf
*sb
= sbuf_make();
813 while (xai
&& ln
&& (*ln
== ' ' || *ln
== '\t'))
815 return sbuf_done(sb
);
818 static void vi_change(int r1
, int o1
, int r2
, int o2
, int lnmode
)
824 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
825 reg_put(vi_ybuf
, region
, lnmode
);
827 pref
= lnmode
? vi_indents(lbuf_get(xb
, r1
)) : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
828 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
829 vi_drawrm(r1
, r2
, 0);
830 rep
= vi_input(pref
, post
, &row
, &off
);
832 lbuf_edit(xb
, rep
, r1
, r2
+ 1);
841 static void vi_case(int r1
, int o1
, int r2
, int o2
, int lnmode
, int cmd
)
845 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
848 int c
= (unsigned char) s
[0];
855 s
[0] = islower(c
) ? toupper(c
) : tolower(c
);
859 pref
= lnmode
? uc_dup("") : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
860 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
862 struct sbuf
*sb
= sbuf_make();
864 sbuf_str(sb
, region
);
866 lbuf_edit(xb
, sbuf_buf(sb
), r1
, r2
+ 1);
869 lbuf_edit(xb
, region
, r1
, r2
+ 1);
872 xoff
= lnmode
? lbuf_indents(xb
, r2
) : o2
;
878 static void vi_pipe(int r1
, int r2
)
883 char *cmd
= vi_prompt("!", &kmap
, reg_getln('!'));
886 reg_put('!', cmd
, 1);
888 text
= lbuf_cp(xb
, r1
, r2
+ 1);
889 rep
= cmd_pipe(cmd
, text
, 1);
891 lbuf_edit(xb
, rep
, r1
, r2
+ 1);
897 static void vi_shift(int r1
, int r2
, int dir
)
902 for (i
= r1
; i
<= r2
; i
++) {
903 if (!(ln
= lbuf_get(xb
, i
)))
910 ln
= ln
[0] == ' ' || ln
[0] == '\t' ? ln
+ 1 : ln
;
913 lbuf_edit(xb
, sbuf_buf(sb
), i
, i
+ 1);
917 xoff
= lbuf_indents(xb
, xrow
);
920 static int vc_motion(int cmd
)
922 int r1
= xrow
, r2
= xrow
; /* region rows */
923 int o1
= xoff
, o2
= xoff
; /* visual region columns */
924 int lnmode
= 0; /* line-based region */
926 vi_arg2
= vi_prefix();
929 o1
= ren_noeol(lbuf_get(xb
, r1
), o1
);
931 if ((mv
= vi_motionln(&r2
, cmd
))) {
933 } else if (!(mv
= vi_motion(&r2
, &o2
))) {
942 o2
= lbuf_eol(xb
, r2
);
948 if (r1
== r2
&& o1
> o2
)
950 o1
= ren_noeol(lbuf_get(xb
, r1
), o1
);
951 if (!lnmode
&& strchr("fFtTeE%", mv
))
952 if (o2
< lbuf_eol(xb
, r2
))
953 o2
= ren_noeol(lbuf_get(xb
, r2
), o2
) + 1;
955 vi_yank(r1
, o1
, r2
, o2
, lnmode
);
957 vi_delete(r1
, o1
, r2
, o2
, lnmode
);
959 vi_change(r1
, o1
, r2
, o2
, lnmode
);
960 if (cmd
== '~' || cmd
== 'u' || cmd
== 'U')
961 vi_case(r1
, o1
, r2
, o2
, lnmode
, cmd
);
962 if (cmd
== '>' || cmd
== '<')
963 vi_shift(r1
, r2
, cmd
== '>' ? +1 : -1);
965 if (mv
== '{' || mv
== '}')
966 if (lbuf_get(xb
, r2
) && lbuf_get(xb
, r2
)[0] == '\n' && r1
< r2
)
973 static int vc_insert(int cmd
)
976 char *ln
= lbuf_get(xb
, xrow
);
980 xoff
= lbuf_indents(xb
, xrow
);
982 xoff
= lbuf_eol(xb
, xrow
);
983 xoff
= ren_noeol(ln
, xoff
);
986 if (cmd
== 'i' || cmd
== 'I')
988 if (cmd
== 'a' || cmd
== 'A')
990 if (ln
&& ln
[0] == '\n')
992 pref
= ln
&& cmd
!= 'o' && cmd
!= 'O' ? uc_sub(ln
, 0, off
) : vi_indents(ln
);
993 post
= ln
&& cmd
!= 'o' && cmd
!= 'O' ? uc_sub(ln
, off
, -1) : uc_dup("\n");
994 vi_drawrm(xrow
, xrow
, cmd
== 'o' || cmd
== 'O');
995 rep
= vi_input(pref
, post
, &row
, &off
);
996 if ((cmd
== 'o' || cmd
== 'O') && !lbuf_len(xb
))
997 lbuf_edit(xb
, "\n", 0, 0);
999 lbuf_edit(xb
, rep
, xrow
, xrow
+ (cmd
!= 'o' && cmd
!= 'O'));
1009 static int vc_put(int cmd
)
1011 int cnt
= MAX(1, vi_arg1
);
1013 char *buf
= reg_get(vi_ybuf
, &lnmode
);
1016 snprintf(vi_msg
, sizeof(vi_msg
), "yank buffer empty");
1017 if (!buf
|| !buf
[0])
1020 struct sbuf
*sb
= sbuf_make();
1021 for (i
= 0; i
< cnt
; i
++)
1024 lbuf_edit(xb
, "\n", 0, 0);
1027 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
);
1028 xoff
= lbuf_indents(xb
, xrow
);
1031 struct sbuf
*sb
= sbuf_make();
1032 char *ln
= xrow
< lbuf_len(xb
) ? lbuf_get(xb
, xrow
) : "\n";
1033 int off
= ren_noeol(ln
, xoff
) + (ln
[0] != '\n' && cmd
== 'p');
1034 char *s
= uc_sub(ln
, 0, off
);
1037 for (i
= 0; i
< cnt
; i
++)
1039 s
= uc_sub(ln
, off
, -1);
1042 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
+ 1);
1043 xoff
= off
+ uc_slen(buf
) * cnt
- 1;
1049 static int join_spaces(char *prev
, char *next
)
1051 int prevlen
= strlen(prev
);
1054 if (prev
[prevlen
- 1] == ' ' || next
[0] == ')')
1056 return prev
[prevlen
- 1] == '.' ? 2 : 1;
1059 static int vc_join(void)
1062 int cnt
= vi_arg1
<= 1 ? 2 : vi_arg1
;
1064 int end
= xrow
+ cnt
;
1067 if (!lbuf_get(xb
, beg
) || !lbuf_get(xb
, end
- 1))
1070 for (i
= beg
; i
< end
; i
++) {
1071 char *ln
= lbuf_get(xb
, i
);
1072 char *lnend
= strchr(ln
, '\n');
1075 while (ln
[0] == ' ' || ln
[0] == '\t')
1077 spaces
= i
> beg
? join_spaces(sbuf_buf(sb
), ln
) : 0;
1078 off
= uc_slen(sbuf_buf(sb
));
1081 sbuf_mem(sb
, ln
, lnend
- ln
);
1084 lbuf_edit(xb
, sbuf_buf(sb
), beg
, end
);
1090 static int vi_scrollforward(int cnt
)
1092 if (xtop
>= lbuf_len(xb
) - 1)
1094 xtop
= MIN(lbuf_len(xb
) - 1, xtop
+ cnt
);
1095 xrow
= MAX(xrow
, xtop
);
1099 static int vi_scrollbackward(int cnt
)
1103 xtop
= MAX(0, xtop
- cnt
);
1104 xrow
= MIN(xrow
, xtop
+ xrows
- 1);
1108 static void vc_status(void)
1110 int col
= vi_off2col(xb
, xrow
, xoff
);
1111 int win
= w_tmp
? '-' : '=';
1112 snprintf(vi_msg
, sizeof(vi_msg
),
1113 "\"%s\"%c [%c%d] L%d C%d",
1114 ex_path()[0] ? ex_path() : "unnamed",
1115 lbuf_modified(xb
) ? '*' : ' ',
1116 win
, lbuf_len(xb
), xrow
+ 1,
1117 ren_cursor(lbuf_get(xb
, xrow
), col
) + 1);
1120 static void vc_charinfo(void)
1122 char *c
= uc_chr(lbuf_get(xb
, xrow
), xoff
);
1125 memcpy(cbuf
, c
, uc_len(c
));
1126 snprintf(vi_msg
, sizeof(vi_msg
), "<%s> %04x", cbuf
, uc_code(c
));
1130 static int vc_replace(void)
1132 int cnt
= MAX(1, vi_arg1
);
1133 char *cs
= vi_char();
1134 char *ln
= lbuf_get(xb
, xrow
);
1141 off
= ren_noeol(ln
, xoff
);
1142 s
= uc_chr(ln
, off
);
1143 for (i
= 0; s
[0] != '\n' && i
< cnt
; i
++)
1147 pref
= uc_sub(ln
, 0, off
);
1148 post
= uc_sub(ln
, off
+ cnt
, -1);
1151 for (i
= 0; i
< cnt
; i
++)
1154 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
+ 1);
1163 static void vi_marksave(void)
1165 lbuf_mark(xb
, '\'', xrow
, xoff
);
1166 lbuf_mark(xb
, '`', xrow
, xoff
);
1169 static int vc_definition(int newwin
)
1171 char cw
[256], kw
[256];
1175 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "") != 0)
1177 snprintf(kw
, sizeof(kw
), conf_definition(ex_filetype()), cw
);
1178 if (lbuf_search(xb
, kw
, +1, &r
, &o
, &len
) != 0) {
1179 snprintf(vi_msg
, sizeof(vi_msg
), "not found <%s>", kw
);
1182 ln
= lbuf_get(xb
, r
);
1183 if ((s
= strstr(ln
, cw
)) != NULL
)
1195 static int vi_openpath(char *path
, int ln
, int newwin
)
1198 char *sep
= strchr(path
, ':');
1201 if (access(path
, R_OK
) != 0) {
1202 snprintf(vi_msg
, sizeof(vi_msg
), "cannot open <%s>", path
);
1207 snprintf(ex
, sizeof(ex
), "e %s", path
);
1210 if (ln
&& sep
&& isdigit((unsigned char) sep
[1])) {
1211 char *col
= strchr(sep
+ 1, ':');
1212 int lnum
= atoi(sep
+ 1);
1214 xrow
= MIN(MAX(0, lnum
- 1), lbuf_len(xb
) - 1);
1215 if (col
&& isdigit((unsigned char) col
[1]))
1216 xoff
= ren_noeol(lbuf_get(xb
, xrow
), atoi(col
+ 1) - 1);
1221 static int vc_openpath(int ln
, int newwin
)
1224 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "-/.:") != 0)
1226 return vi_openpath(cw
, ln
, newwin
);
1229 static int vc_tag(int newwin
)
1231 char cw
[120], ex
[128];
1232 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "") != 0)
1234 snprintf(ex
, sizeof(ex
), "ta %s", cw
);
1236 if (ex_command(ex
) != 0)
1247 static char rep_cmd
[4096]; /* the last command */
1250 static void vc_repeat(void)
1253 for (i
= 0; i
< MAX(1, vi_arg1
); i
++)
1254 term_push(rep_cmd
, rep_len
);
1257 static void vc_execute(void)
1259 static int reg
= -1;
1270 buf
= reg_get(reg
, &lnmode
);
1272 for (i
= 0; i
< MAX(1, vi_arg1
); i
++)
1273 term_push(buf
, strlen(buf
));
1277 static int vc_ecmd(int c
, int newwin
)
1281 snprintf(cmd
, sizeof(cmd
), "%s %c %s %d %d",
1282 conf_ecmd(), c
, ex_path(), xrow
+ 1, xoff
+ 1);
1283 if ((out
= cmd_pipe(cmd
, NULL
, 2)) == NULL
) {
1284 snprintf(vi_msg
, sizeof(vi_msg
), "command failed");
1287 if (!strchr(out
, '\n')) {
1288 snprintf(vi_msg
, sizeof(vi_msg
), "no output");
1301 static void sigwinch(int signo
)
1303 vi_back(TK_CTL('l'));
1304 vi_back(TK_CTL('c'));
1307 static void vi(void)
1313 signal(SIGWINCH
, sigwinch
);
1315 xtop
= MAX(0, xrow
- xrows
/ 2);
1317 xcol
= vi_off2col(xb
, xrow
, xoff
);
1318 vi_drawagain(xcol
, 0);
1319 term_pos(xrow
- xtop
, led_pos(lbuf_get(xb
, xrow
), xcol
));
1321 int mod
= 0; /* redrawn the screen (1: window, 2: current line, 4: other windows) */
1323 int noff
= ren_noeol(lbuf_get(xb
, xrow
), xoff
);
1327 char *opath
= ex_path(); /* do not dereference; to detect buffer changes */
1331 vi_ybuf
= vi_yankbuf();
1332 vi_arg1
= vi_prefix();
1334 vi_ybuf
= vi_yankbuf();
1335 mv
= vi_motion(&nrow
, &noff
);
1337 if (strchr("\'`GHML/?{}[]nN", mv
) || (mv
== '%' && noff
< 0))
1340 if (noff
< 0 && !strchr("jk", mv
))
1341 noff
= lbuf_indents(xb
, xrow
);
1342 if (strchr("jk", mv
))
1343 noff
= vi_col2off(xb
, xrow
, xcol
);
1344 xoff
= ren_noeol(lbuf_get(xb
, xrow
), noff
);
1345 if (!strchr("|jk", mv
))
1346 xcol
= vi_off2col(xb
, xrow
, xoff
);
1349 } else if (mv
== 0) {
1355 lbuf_mark(xb
, '*', xrow
, xoff
);
1358 if (vi_scrollbackward(MAX(1, vi_arg1
) * (xrows
- 1)))
1360 xoff
= lbuf_indents(xb
, xrow
);
1364 if (vi_scrollforward(MAX(1, vi_arg1
) * (xrows
- 1)))
1366 xoff
= lbuf_indents(xb
, xrow
);
1370 if (vi_scrollforward(MAX(1, vi_arg1
)))
1372 xoff
= vi_col2off(xb
, xrow
, xcol
);
1375 if (vi_scrollbackward(MAX(1, vi_arg1
)))
1377 xoff
= vi_col2off(xb
, xrow
, xcol
);
1383 vi_scroll
= vi_arg1
;
1384 n
= vi_scroll
? vi_scroll
: xrows
/ 2;
1385 xrow
= MAX(0, xrow
- n
);
1387 xtop
= MAX(0, xtop
- n
);
1388 xoff
= lbuf_indents(xb
, xrow
);
1392 if (xrow
== lbuf_len(xb
) - 1)
1395 vi_scroll
= vi_arg1
;
1396 n
= vi_scroll
? vi_scroll
: xrows
/ 2;
1397 xrow
= MIN(MAX(0, lbuf_len(xb
) - 1), xrow
+ n
);
1398 if (xtop
< lbuf_len(xb
) - xrows
)
1399 xtop
= MIN(lbuf_len(xb
) - xrows
, xtop
+ n
);
1400 xoff
= lbuf_indents(xb
, xrow
);
1409 if (!lbuf_undo(xb
)) {
1410 lbuf_jump(xb
, '*', &xrow
, &xoff
);
1413 snprintf(vi_msg
, sizeof(vi_msg
), "undo failed");
1417 if (!lbuf_redo(xb
)) {
1418 lbuf_jump(xb
, '*', &xrow
, &xoff
);
1421 snprintf(vi_msg
, sizeof(vi_msg
), "redo failed");
1436 if (!ex_command("pop")) {
1446 if (k
== 'j' || k
== 'k') {
1448 vi_switch(1 - w_cur
);
1461 if (k
== TK_CTL(']') || k
== ']')
1466 if (j
== 'f' || j
== 'l')
1467 if (!vc_openpath(j
== 'l', 1))
1470 if (!vc_definition(1))
1475 if (isalpha(j
) && !vc_ecmd(j
, 1))
1480 ln
= vi_prompt(":", &kmap
, reg_getln(':'));
1484 char *ln2
= uc_cat(":", ln
);
1489 if (!ex_command(ln
))
1492 reg_put(':', ln
, 1);
1526 if ((mark
= vi_read()) > 0 && islower(mark
))
1527 lbuf_mark(xb
, mark
, xrow
, xoff
);
1538 xtop
= vi_arg1
? vi_arg1
: xrow
;
1542 n
= vi_arg1
? vi_arg1
: xrow
;
1543 xtop
= MAX(0, n
- xrows
/ 2);
1547 n
= vi_arg1
? vi_arg1
: xrow
;
1548 xtop
= MAX(0, n
- xrows
+ 1);
1553 xtd
= k
== 'r' ? -1 : +1;
1558 xtd
= k
== 'R' ? -2 : +2;
1563 xkmap
= k
== 'e' ? 0 : xkmap_alt
;
1567 if (!ex_command(k
== 'j' ? "b +" : "b -"))
1572 if (!ex_command(k
== 'J' ? "next" : "prev"))
1576 if (!ex_command("b !"))
1583 if (k
== '~' || k
== 'u' || k
== 'U')
1589 if (!vc_definition(0))
1591 if (k
== 'f' || k
== 'l')
1592 if (!vc_openpath(k
== 'l', 0))
1597 if (!vc_motion('d'))
1601 vi_back(TK_CTL('h'));
1602 if (!vc_motion('d'))
1607 if (!vc_motion('c'))
1612 if (!vc_motion('d'))
1617 if (isalpha(k
) && !vc_ecmd(k
, 0))
1626 if (!vc_motion('c'))
1631 if (!vc_motion('c'))
1641 if (!ex_command("x"))
1646 if (!vc_motion('~'))
1659 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c
) ||
1660 (c
== 'g' && strchr("uU~", k
))) {
1661 if (n
+ 1 < sizeof(rep_cmd
)) {
1662 memcpy(rep_cmd
, cmd
, n
);
1665 reg_put('.', rep_cmd
, 0);
1671 xcol
= vi_off2col(xb
, xrow
, xoff
);
1672 if (xcol
>= xleft
+ xcols
)
1673 xleft
= xcol
- xcols
/ 2;
1675 xleft
= xcol
< xcols
? 0 : xcol
- xcols
/ 2;
1677 ru
= (xru
& 1) || ((xru
& 2) && w_cnt
> 1) || ((xru
& 4) && opath
!= ex_path());
1678 if (mod
& 4 && w_cnt
> 1) {
1679 char msg
[sizeof(vi_msg
)];
1684 strcpy(msg
, vi_msg
);
1687 vi_drawagain(vi_off2col(xb
, xrow
, xoff
), 0);
1688 strcpy(vi_msg
, msg
);
1692 if (ru
&& !vi_msg
[0])
1694 if (mod
|| xleft
!= oleft
) {
1695 vi_drawagain(xcol
, mod
== 2 && xleft
== oleft
&& xrow
== orow
);
1698 vi_drawupdate(xcol
, otop
);
1699 if (xhll
&& xrow
!= orow
&& orow
>= xtop
&& orow
< xtop
+ xrows
)
1701 if (xhll
&& xrow
!= orow
)
1706 term_pos(xrow
- xtop
, led_pos(lbuf_get(xb
, xrow
),
1707 ren_cursor(lbuf_get(xb
, xrow
), xcol
)));
1712 int main(int argc
, char *argv
[])
1715 char *prog
= strchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
1716 xvis
= strcmp("ex", prog
) && strcmp("neatex", prog
);
1717 for (i
= 1; i
< argc
&& argv
[i
][0] == '-'; i
++) {
1718 if (argv
[i
][1] == 's')
1720 if (argv
[i
][1] == 'e')
1722 if (argv
[i
][1] == 'v')
1724 if (argv
[i
][1] == 'h') {
1725 printf("usage: %s [options] [file...]\n\n", argv
[0]);
1726 printf("options:\n");
1727 printf(" -v start in vi mode\n");
1728 printf(" -e start in ex mode\n");
1729 printf(" -s silent mode (for ex mode only)\n");
1736 if (!ex_init(argv
+ i
)) {