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
? "---" : "___"));
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
)
221 term_pos(xrows
, led_pos(msg
, 0));
223 s
= led_prompt(msg
, "", kmap
, xhl
? "---" : "___");
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 char *s
= led_prompt(msg
, "", &xkmap
, xhl
? "---" : "___");
243 while ((c
= getchar()) != EOF
&& c
!= '\n')
249 return sbuf_done(sb
);
252 /* show an ex message */
253 void ex_show(char *msg
)
256 snprintf(vi_msg
, sizeof(vi_msg
), "%s", msg
);
258 led_print(msg
, -1, xhl
? "---" : "___");
265 /* print an ex output line */
266 void ex_print(char *line
)
269 if (line
&& vi_printed
== 0)
270 snprintf(vi_msg
, sizeof(vi_msg
), "%s", line
);
271 if (line
&& vi_printed
== 1)
272 led_print(vi_msg
, xrows
- 1, xhl
? "-ex" : "");
275 if (line
&& vi_printed
)
276 led_print(line
, xrows
- 1, xhl
? "-ex" : "");
277 vi_printed
+= line
!= NULL
? 1 : -1000;
284 static int vi_yankbuf(void)
293 static int vi_prefix(void)
297 if ((c
>= '1' && c
<= '9')) {
299 n
= n
* 10 + c
- '0';
307 static int vi_col2off(struct lbuf
*lb
, int row
, int col
)
309 char *ln
= lbuf_get(lb
, row
);
310 return ln
? ren_off(ln
, col
) : 0;
313 static int vi_off2col(struct lbuf
*lb
, int row
, int off
)
315 char *ln
= lbuf_get(lb
, row
);
316 return ln
? ren_pos(ln
, off
) : 0;
319 static int vi_nextoff(struct lbuf
*lb
, int dir
, int *row
, int *off
)
322 if (o
< 0 || !lbuf_get(lb
, *row
) || o
>= uc_slen(lbuf_get(lb
, *row
)))
328 static int vi_nextcol(struct lbuf
*lb
, int dir
, int *row
, int *off
)
330 char *ln
= lbuf_get(lb
, *row
);
331 int col
= ln
? ren_pos(ln
, *off
) : 0;
332 int o
= ln
? ren_next(ln
, col
, dir
) : -1;
335 *off
= ren_off(ln
, o
);
339 static int vi_findchar(struct lbuf
*lb
, char *cs
, int cmd
, int n
, int *row
, int *off
)
341 if (cs
!= vi_charlast
)
342 strcpy(vi_charlast
, cs
);
344 return lbuf_findchar(lb
, cs
, cmd
, n
, row
, off
);
347 static int vi_search(int cmd
, int cnt
, int *row
, int *off
)
355 if (cmd
== '/' || cmd
== '?') {
356 char sign
[4] = {cmd
};
358 char *kw
= vi_prompt(sign
, &xkmap
);
367 if ((re
= re_read(&kw
))) {
368 ex_kwdset(re
[0] ? re
: NULL
, cmd
== '/' ? +1 : -1);
377 if (!lbuf_len(xb
) || ex_kwd(&kwd
, &dir
))
379 dir
= cmd
== 'N' ? -dir
: dir
;
381 for (i
= 0; i
< cnt
; i
++) {
382 if (lbuf_search(xb
, kwd
, dir
, &r
, &o
, &len
)) {
383 failed
= " not found";
386 if (i
+ 1 < cnt
&& cmd
== '/')
394 if (*row
+ vi_so
< 0 || *row
+ vi_so
>= lbuf_len(xb
))
395 failed
= " bad offset";
401 snprintf(vi_msg
, sizeof(vi_msg
), "/%s/%s", kwd
, failed
? failed
: "");
402 return failed
!= NULL
;
405 /* read a line motion */
406 static int vi_motionln(int *row
, int cmd
)
408 int cnt
= (vi_arg1
? vi_arg1
: 1) * (vi_arg2
? vi_arg2
: 1);
410 int mark
, mark_row
, mark_off
;
414 *row
= MIN(*row
+ cnt
, lbuf_len(xb
) - 1);
417 *row
= MAX(*row
- cnt
, 0);
420 *row
= MIN(*row
+ cnt
- 1, lbuf_len(xb
) - 1);
423 if ((mark
= vi_read()) <= 0)
425 if (lbuf_jump(xb
, mark
, &mark_row
, &mark_off
))
430 *row
= MIN(*row
+ cnt
, lbuf_len(xb
) - 1);
433 *row
= MAX(*row
- cnt
, 0);
436 *row
= (vi_arg1
|| vi_arg2
) ? cnt
- 1 : lbuf_len(xb
) - 1;
439 *row
= MIN(xtop
+ cnt
- 1, lbuf_len(xb
) - 1);
442 *row
= MIN(xtop
+ xrows
- 1 - cnt
+ 1, lbuf_len(xb
) - 1);
445 *row
= MIN(xtop
+ xrows
/ 2, lbuf_len(xb
) - 1);
449 *row
= MIN(*row
+ cnt
- 1, lbuf_len(xb
) - 1);
452 if (c
== '%' && (vi_arg1
|| vi_arg2
)) {
455 *row
= MAX(0, lbuf_len(xb
) - 1) * cnt
/ 100;
466 static int vi_curword(struct lbuf
*lb
, char *dst
, int len
, int row
, int off
, char *ext
)
468 char *ln
= lbuf_get(lb
, row
);
472 beg
= uc_chr(ln
, ren_noeol(ln
, off
));
474 while (*end
&& (uc_kind(end
) == 1 ||
475 strchr(ext
, (unsigned char) end
[0]) != NULL
))
477 while (beg
> ln
&& (uc_kind(uc_beg(ln
, beg
- 1)) == 1 ||
478 strchr(ext
, (unsigned char) beg
[-1]) != NULL
))
479 beg
= uc_beg(ln
, beg
- 1);
482 len
= len
- 1 < end
- beg
? len
- 1 : end
- beg
;
484 memcpy(dst
, beg
, len
);
489 static int vi_motion(int *row
, int *off
)
491 char cw
[120], kw
[128];
492 int cnt
= (vi_arg1
? vi_arg1
: 1) * (vi_arg2
? vi_arg2
: 1);
493 char *ln
= lbuf_get(xb
, *row
);
494 int dir
= dir_context(ln
? ln
: "");
495 int mark
, mark_row
, mark_off
;
499 if ((mv
= vi_motionln(row
, 0))) {
506 if (!(cs
= vi_char()))
508 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
512 if (!(cs
= vi_char()))
514 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
520 if (vi_findchar(xb
, vi_charlast
, vi_charcmd
, cnt
, row
, off
))
526 if (vi_findchar(xb
, vi_charlast
, vi_charcmd
, -cnt
, row
, off
))
530 for (i
= 0; i
< cnt
; i
++)
531 if (vi_nextcol(xb
, -1 * dir
, row
, off
))
535 for (i
= 0; i
< cnt
; i
++)
536 if (vi_nextcol(xb
, +1 * dir
, row
, off
))
540 if (!(cs
= vi_char()))
542 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
546 if (!(cs
= vi_char()))
548 if (vi_findchar(xb
, cs
, mv
, cnt
, row
, off
))
552 for (i
= 0; i
< cnt
; i
++)
553 if (lbuf_wordend(xb
, 1, -1, row
, off
))
557 for (i
= 0; i
< cnt
; i
++)
558 if (lbuf_wordend(xb
, 1, +1, row
, off
))
562 for (i
= 0; i
< cnt
; i
++)
563 if (lbuf_wordbeg(xb
, 1, +1, row
, off
))
567 for (i
= 0; i
< cnt
; i
++)
568 if (lbuf_wordend(xb
, 0, -1, row
, off
))
572 for (i
= 0; i
< cnt
; i
++)
573 if (lbuf_wordend(xb
, 0, +1, row
, off
))
577 for (i
= 0; i
< cnt
; i
++)
578 if (lbuf_wordbeg(xb
, 0, +1, row
, off
))
582 for (i
= 0; i
< cnt
; i
++)
583 if (lbuf_paragraphbeg(xb
, -1, row
, off
))
587 for (i
= 0; i
< cnt
; i
++)
588 if (lbuf_paragraphbeg(xb
, +1, row
, off
))
592 if (vi_read() != '[')
594 for (i
= 0; i
< cnt
; i
++)
595 if (lbuf_sectionbeg(xb
, -1, conf_section(ex_filetype()), row
, off
))
599 if (vi_read() != ']')
601 for (i
= 0; i
< cnt
; i
++)
602 if (lbuf_sectionbeg(xb
, +1, conf_section(ex_filetype()), row
, off
))
609 *off
= lbuf_indents(xb
, *row
);
612 *off
= lbuf_eol(xb
, *row
);
615 *off
= vi_col2off(xb
, *row
, cnt
- 1);
619 if (vi_search(mv
, cnt
, row
, off
))
623 if (vi_search(mv
, cnt
, row
, off
))
627 if (vi_search(mv
, cnt
, row
, off
))
631 if (vi_search(mv
, cnt
, row
, off
))
635 if (vi_curword(xb
, cw
, sizeof(cw
), *row
, *off
, "") != 0)
637 snprintf(kw
, sizeof(kw
), "\\<%s\\>", cw
);
640 if (vi_search('n', cnt
, row
, off
))
644 for (i
= 0; i
< cnt
; i
++)
645 if (vi_nextoff(xb
, +1, row
, off
))
650 for (i
= 0; i
< cnt
; i
++)
651 if (vi_nextoff(xb
, -1, row
, off
))
655 if ((mark
= vi_read()) <= 0)
657 if (lbuf_jump(xb
, mark
, &mark_row
, &mark_off
))
663 if (lbuf_pair(xb
, row
, off
))
673 static void swap(int *a
, int *b
)
680 static char *lbuf_region(struct lbuf
*lb
, int r1
, int o1
, int r2
, int o2
)
685 return uc_sub(lbuf_get(lb
, r1
), o1
, o2
);
687 s1
= uc_sub(lbuf_get(lb
, r1
), o1
, -1);
688 s3
= uc_sub(lbuf_get(lb
, r2
), 0, o2
);
689 s2
= lbuf_cp(lb
, r1
+ 1, r2
);
696 return sbuf_done(sb
);
699 static void vi_yank(int r1
, int o1
, int r2
, int o2
, int lnmode
)
702 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
703 reg_put(vi_ybuf
, region
, lnmode
);
706 xoff
= lnmode
? xoff
: o1
;
709 static void vi_delete(int r1
, int o1
, int r2
, int o2
, int lnmode
)
713 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
714 reg_put(vi_ybuf
, region
, lnmode
);
716 pref
= lnmode
? uc_dup("") : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
717 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
719 char *line
= uc_cat(pref
, post
);
720 lbuf_edit(xb
, line
, r1
, r2
+ 1);
723 lbuf_edit(xb
, NULL
, r1
, r2
+ 1);
726 xoff
= lnmode
? lbuf_indents(xb
, xrow
) : o1
;
731 static int linecount(char *s
)
735 if ((s
= strchr(s
, '\n')))
740 static int charcount(char *text
, char *post
)
742 int tlen
= strlen(text
);
743 int plen
= strlen(post
);
748 for (i
= 0; i
< tlen
- plen
; i
++)
751 return uc_slen(nl
) - uc_slen(post
);
754 static char *vi_input(char *pref
, char *post
, int *row
, int *off
)
756 char *rep
= led_input(pref
, post
, &xkmap
, xhl
? ex_filetype() : "");
759 *row
= linecount(rep
) - 1;
760 *off
= charcount(rep
, post
) - 1;
766 static char *vi_indents(char *ln
)
768 struct sbuf
*sb
= sbuf_make();
769 while (xai
&& ln
&& (*ln
== ' ' || *ln
== '\t'))
771 return sbuf_done(sb
);
774 static void vi_change(int r1
, int o1
, int r2
, int o2
, int lnmode
)
780 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
781 reg_put(vi_ybuf
, region
, lnmode
);
783 pref
= lnmode
? vi_indents(lbuf_get(xb
, r1
)) : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
784 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
785 vi_drawrm(r1
, r2
, 0);
786 rep
= vi_input(pref
, post
, &row
, &off
);
788 lbuf_edit(xb
, rep
, r1
, r2
+ 1);
797 static void vi_case(int r1
, int o1
, int r2
, int o2
, int lnmode
, int cmd
)
801 region
= lbuf_region(xb
, r1
, lnmode
? 0 : o1
, r2
, lnmode
? -1 : o2
);
804 int c
= (unsigned char) s
[0];
811 s
[0] = islower(c
) ? toupper(c
) : tolower(c
);
815 pref
= lnmode
? uc_dup("") : uc_sub(lbuf_get(xb
, r1
), 0, o1
);
816 post
= lnmode
? uc_dup("\n") : uc_sub(lbuf_get(xb
, r2
), o2
, -1);
818 struct sbuf
*sb
= sbuf_make();
820 sbuf_str(sb
, region
);
822 lbuf_edit(xb
, sbuf_buf(sb
), r1
, r2
+ 1);
825 lbuf_edit(xb
, region
, r1
, r2
+ 1);
828 xoff
= lnmode
? lbuf_indents(xb
, r2
) : o2
;
834 static void vi_pipe(int r1
, int r2
)
839 char *cmd
= vi_prompt("!", &kmap
);
842 text
= lbuf_cp(xb
, r1
, r2
+ 1);
843 rep
= cmd_pipe(cmd
, text
, 1);
845 lbuf_edit(xb
, rep
, r1
, r2
+ 1);
851 static void vi_shift(int r1
, int r2
, int dir
)
856 for (i
= r1
; i
<= r2
; i
++) {
857 if (!(ln
= lbuf_get(xb
, i
)))
864 ln
= ln
[0] == ' ' || ln
[0] == '\t' ? ln
+ 1 : ln
;
867 lbuf_edit(xb
, sbuf_buf(sb
), i
, i
+ 1);
871 xoff
= lbuf_indents(xb
, xrow
);
874 static int vc_motion(int cmd
)
876 int r1
= xrow
, r2
= xrow
; /* region rows */
877 int o1
= xoff
, o2
= xoff
; /* visual region columns */
878 int lnmode
= 0; /* line-based region */
880 vi_arg2
= vi_prefix();
883 o1
= ren_noeol(lbuf_get(xb
, r1
), o1
);
885 if ((mv
= vi_motionln(&r2
, cmd
))) {
887 } else if (!(mv
= vi_motion(&r2
, &o2
))) {
896 o2
= lbuf_eol(xb
, r2
);
902 if (r1
== r2
&& o1
> o2
)
904 o1
= ren_noeol(lbuf_get(xb
, r1
), o1
);
905 if (!lnmode
&& strchr("fFtTeE%", mv
))
906 if (o2
< lbuf_eol(xb
, r2
))
907 o2
= ren_noeol(lbuf_get(xb
, r2
), o2
) + 1;
909 vi_yank(r1
, o1
, r2
, o2
, lnmode
);
911 vi_delete(r1
, o1
, r2
, o2
, lnmode
);
913 vi_change(r1
, o1
, r2
, o2
, lnmode
);
914 if (cmd
== '~' || cmd
== 'u' || cmd
== 'U')
915 vi_case(r1
, o1
, r2
, o2
, lnmode
, cmd
);
916 if (cmd
== '>' || cmd
== '<')
917 vi_shift(r1
, r2
, cmd
== '>' ? +1 : -1);
919 if (mv
== '{' || mv
== '}')
920 if (lbuf_get(xb
, r2
) && lbuf_get(xb
, r2
)[0] == '\n' && r1
< r2
)
927 static int vc_insert(int cmd
)
930 char *ln
= lbuf_get(xb
, xrow
);
934 xoff
= lbuf_indents(xb
, xrow
);
936 xoff
= lbuf_eol(xb
, xrow
);
937 xoff
= ren_noeol(ln
, xoff
);
940 if (cmd
== 'i' || cmd
== 'I')
942 if (cmd
== 'a' || cmd
== 'A')
944 if (ln
&& ln
[0] == '\n')
946 pref
= ln
&& cmd
!= 'o' && cmd
!= 'O' ? uc_sub(ln
, 0, off
) : vi_indents(ln
);
947 post
= ln
&& cmd
!= 'o' && cmd
!= 'O' ? uc_sub(ln
, off
, -1) : uc_dup("\n");
948 vi_drawrm(xrow
, xrow
, cmd
== 'o' || cmd
== 'O');
949 rep
= vi_input(pref
, post
, &row
, &off
);
950 if ((cmd
== 'o' || cmd
== 'O') && !lbuf_len(xb
))
951 lbuf_edit(xb
, "\n", 0, 0);
953 lbuf_edit(xb
, rep
, xrow
, xrow
+ (cmd
!= 'o' && cmd
!= 'O'));
963 static int vc_put(int cmd
)
965 int cnt
= MAX(1, vi_arg1
);
967 char *buf
= reg_get(vi_ybuf
, &lnmode
);
970 snprintf(vi_msg
, sizeof(vi_msg
), "yank buffer empty");
974 struct sbuf
*sb
= sbuf_make();
975 for (i
= 0; i
< cnt
; i
++)
978 lbuf_edit(xb
, "\n", 0, 0);
981 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
);
982 xoff
= lbuf_indents(xb
, xrow
);
985 struct sbuf
*sb
= sbuf_make();
986 char *ln
= xrow
< lbuf_len(xb
) ? lbuf_get(xb
, xrow
) : "\n";
987 int off
= ren_noeol(ln
, xoff
) + (ln
[0] != '\n' && cmd
== 'p');
988 char *s
= uc_sub(ln
, 0, off
);
991 for (i
= 0; i
< cnt
; i
++)
993 s
= uc_sub(ln
, off
, -1);
996 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
+ 1);
997 xoff
= off
+ uc_slen(buf
) * cnt
- 1;
1003 static int join_spaces(char *prev
, char *next
)
1005 int prevlen
= strlen(prev
);
1008 if (prev
[prevlen
- 1] == ' ' || next
[0] == ')')
1010 return prev
[prevlen
- 1] == '.' ? 2 : 1;
1013 static int vc_join(void)
1016 int cnt
= vi_arg1
<= 1 ? 2 : vi_arg1
;
1018 int end
= xrow
+ cnt
;
1021 if (!lbuf_get(xb
, beg
) || !lbuf_get(xb
, end
- 1))
1024 for (i
= beg
; i
< end
; i
++) {
1025 char *ln
= lbuf_get(xb
, i
);
1026 char *lnend
= strchr(ln
, '\n');
1029 while (ln
[0] == ' ' || ln
[0] == '\t')
1031 spaces
= i
> beg
? join_spaces(sbuf_buf(sb
), ln
) : 0;
1032 off
= uc_slen(sbuf_buf(sb
));
1035 sbuf_mem(sb
, ln
, lnend
- ln
);
1038 lbuf_edit(xb
, sbuf_buf(sb
), beg
, end
);
1044 static int vi_scrollforward(int cnt
)
1046 if (xtop
>= lbuf_len(xb
) - 1)
1048 xtop
= MIN(lbuf_len(xb
) - 1, xtop
+ cnt
);
1049 xrow
= MAX(xrow
, xtop
);
1053 static int vi_scrollbackward(int cnt
)
1057 xtop
= MAX(0, xtop
- cnt
);
1058 xrow
= MIN(xrow
, xtop
+ xrows
- 1);
1062 static void vc_status(void)
1064 int col
= vi_off2col(xb
, xrow
, xoff
);
1065 int win
= w_tmp
? '-' : '=';
1066 snprintf(vi_msg
, sizeof(vi_msg
),
1067 "\"%s\"%c [%c%d] L%d C%d",
1068 ex_path()[0] ? ex_path() : "unnamed",
1069 lbuf_modified(xb
) ? '*' : ' ',
1070 win
, lbuf_len(xb
), xrow
+ 1,
1071 ren_cursor(lbuf_get(xb
, xrow
), col
) + 1);
1074 static void vc_charinfo(void)
1076 char *c
= uc_chr(lbuf_get(xb
, xrow
), xoff
);
1079 memcpy(cbuf
, c
, uc_len(c
));
1080 snprintf(vi_msg
, sizeof(vi_msg
), "<%s> %04x", cbuf
, uc_code(c
));
1084 static int vc_replace(void)
1086 int cnt
= MAX(1, vi_arg1
);
1087 char *cs
= vi_char();
1088 char *ln
= lbuf_get(xb
, xrow
);
1095 off
= ren_noeol(ln
, xoff
);
1096 s
= uc_chr(ln
, off
);
1097 for (i
= 0; s
[0] != '\n' && i
< cnt
; i
++)
1101 pref
= uc_sub(ln
, 0, off
);
1102 post
= uc_sub(ln
, off
+ cnt
, -1);
1105 for (i
= 0; i
< cnt
; i
++)
1108 lbuf_edit(xb
, sbuf_buf(sb
), xrow
, xrow
+ 1);
1117 static void vi_marksave(void)
1119 lbuf_mark(xb
, '\'', xrow
, xoff
);
1120 lbuf_mark(xb
, '`', xrow
, xoff
);
1123 static int vc_definition(int newwin
)
1125 char cw
[256], kw
[256];
1129 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "") != 0)
1131 snprintf(kw
, sizeof(kw
), conf_definition(ex_filetype()), cw
);
1132 if (lbuf_search(xb
, kw
, +1, &r
, &o
, &len
) != 0) {
1133 snprintf(vi_msg
, sizeof(vi_msg
), "not found <%s>", kw
);
1136 ln
= lbuf_get(xb
, r
);
1137 if ((s
= strstr(ln
, cw
)) != NULL
)
1149 static int vi_openpath(char *path
, int ln
, int newwin
)
1152 char *sep
= strchr(path
, ':');
1155 if (access(path
, R_OK
) != 0) {
1156 snprintf(vi_msg
, sizeof(vi_msg
), "cannot open <%s>", path
);
1161 snprintf(ex
, sizeof(ex
), "e %s", path
);
1164 if (ln
&& sep
&& isdigit((unsigned char) sep
[1])) {
1165 char *col
= strchr(sep
+ 1, ':');
1166 int lnum
= atoi(sep
+ 1);
1168 xrow
= MIN(MAX(0, lnum
- 1), lbuf_len(xb
) - 1);
1169 if (col
&& isdigit((unsigned char) col
[1]))
1170 xoff
= ren_noeol(lbuf_get(xb
, xrow
), atoi(col
+ 1) - 1);
1175 static int vc_openpath(int ln
, int newwin
)
1178 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "-/.:") != 0)
1180 return vi_openpath(cw
, ln
, newwin
);
1183 static int vc_tag(int newwin
)
1185 char cw
[120], ex
[128];
1186 if (vi_curword(xb
, cw
, sizeof(cw
), xrow
, xoff
, "") != 0)
1188 snprintf(ex
, sizeof(ex
), "ta %s", cw
);
1190 if (ex_command(ex
) != 0)
1201 static char rep_cmd
[4096]; /* the last command */
1204 static void vc_repeat(void)
1207 for (i
= 0; i
< MAX(1, vi_arg1
); i
++)
1208 term_push(rep_cmd
, rep_len
);
1211 static void vc_execute(void)
1213 static int reg
= -1;
1224 buf
= reg_get(reg
, &lnmode
);
1226 for (i
= 0; i
< MAX(1, vi_arg1
); i
++) {
1227 term_push(buf
, strlen(buf
));
1228 term_push("\n", lnmode
);
1233 static int vc_ecmd(int c
, int newwin
)
1237 snprintf(cmd
, sizeof(cmd
), "%s %c %s %d %d",
1238 conf_ecmd(), c
, ex_path(), xrow
+ 1, xoff
+ 1);
1239 if ((out
= cmd_pipe(cmd
, NULL
, 2)) == NULL
) {
1240 snprintf(vi_msg
, sizeof(vi_msg
), "command failed");
1243 if (!strchr(out
, '\n')) {
1244 snprintf(vi_msg
, sizeof(vi_msg
), "no output");
1257 static void sigwinch(int signo
)
1259 vi_back(TK_CTL('l'));
1260 vi_back(TK_CTL('c'));
1263 static void vi(void)
1269 signal(SIGWINCH
, sigwinch
);
1271 xtop
= MAX(0, xrow
- xrows
/ 2);
1273 xcol
= vi_off2col(xb
, xrow
, xoff
);
1274 vi_drawagain(xcol
, 0);
1275 term_pos(xrow
- xtop
, led_pos(lbuf_get(xb
, xrow
), xcol
));
1277 int mod
= 0; /* redrawn the screen (1: window, 2: current line, 4: other windows) */
1279 int noff
= ren_noeol(lbuf_get(xb
, xrow
), xoff
);
1283 char *opath
= ex_path(); /* do not dereference; to detect buffer changes */
1287 vi_ybuf
= vi_yankbuf();
1288 vi_arg1
= vi_prefix();
1290 vi_ybuf
= vi_yankbuf();
1291 mv
= vi_motion(&nrow
, &noff
);
1293 if (strchr("\'`GHML/?{}[]nN", mv
) || (mv
== '%' && noff
< 0))
1296 if (noff
< 0 && !strchr("jk", mv
))
1297 noff
= lbuf_indents(xb
, xrow
);
1298 if (strchr("jk", mv
))
1299 noff
= vi_col2off(xb
, xrow
, xcol
);
1300 xoff
= ren_noeol(lbuf_get(xb
, xrow
), noff
);
1301 if (!strchr("|jk", mv
))
1302 xcol
= vi_off2col(xb
, xrow
, xoff
);
1305 } else if (mv
== 0) {
1311 lbuf_mark(xb
, '*', xrow
, xoff
);
1314 if (vi_scrollbackward(MAX(1, vi_arg1
) * (xrows
- 1)))
1316 xoff
= lbuf_indents(xb
, xrow
);
1320 if (vi_scrollforward(MAX(1, vi_arg1
) * (xrows
- 1)))
1322 xoff
= lbuf_indents(xb
, xrow
);
1326 if (vi_scrollforward(MAX(1, vi_arg1
)))
1328 xoff
= vi_col2off(xb
, xrow
, xcol
);
1331 if (vi_scrollbackward(MAX(1, vi_arg1
)))
1333 xoff
= vi_col2off(xb
, xrow
, xcol
);
1339 vi_scroll
= vi_arg1
;
1340 n
= vi_scroll
? vi_scroll
: xrows
/ 2;
1341 xrow
= MAX(0, xrow
- n
);
1343 xtop
= MAX(0, xtop
- n
);
1344 xoff
= lbuf_indents(xb
, xrow
);
1348 if (xrow
== lbuf_len(xb
) - 1)
1351 vi_scroll
= vi_arg1
;
1352 n
= vi_scroll
? vi_scroll
: xrows
/ 2;
1353 xrow
= MIN(MAX(0, lbuf_len(xb
) - 1), xrow
+ n
);
1354 if (xtop
< lbuf_len(xb
) - xrows
)
1355 xtop
= MIN(lbuf_len(xb
) - xrows
, xtop
+ n
);
1356 xoff
= lbuf_indents(xb
, xrow
);
1365 if (!lbuf_undo(xb
)) {
1366 lbuf_jump(xb
, '*', &xrow
, &xoff
);
1369 snprintf(vi_msg
, sizeof(vi_msg
), "undo failed");
1373 if (!lbuf_redo(xb
)) {
1374 lbuf_jump(xb
, '*', &xrow
, &xoff
);
1377 snprintf(vi_msg
, sizeof(vi_msg
), "redo failed");
1392 if (!ex_command("pop")) {
1402 if (k
== 'j' || k
== 'k') {
1404 vi_switch(1 - w_cur
);
1417 if (k
== TK_CTL(']') || k
== ']')
1422 if (j
== 'f' || j
== 'l')
1423 if (!vc_openpath(j
== 'l', 1))
1426 if (!vc_definition(1))
1431 if (isalpha(j
) && !vc_ecmd(j
, 1))
1436 ln
= vi_prompt(":", &kmap
);
1439 char *ln2
= uc_cat(":", ln
);
1444 if (!ex_command(ln
))
1447 reg_put(':', ln
, 1);
1481 if ((mark
= vi_read()) > 0 && islower(mark
))
1482 lbuf_mark(xb
, mark
, xrow
, xoff
);
1493 xtop
= vi_arg1
? vi_arg1
: xrow
;
1497 n
= vi_arg1
? vi_arg1
: xrow
;
1498 xtop
= MAX(0, n
- xrows
/ 2);
1502 n
= vi_arg1
? vi_arg1
: xrow
;
1503 xtop
= MAX(0, n
- xrows
+ 1);
1508 xtd
= k
== 'r' ? -1 : +1;
1513 xtd
= k
== 'R' ? -2 : +2;
1518 xkmap
= k
== 'e' ? 0 : xkmap_alt
;
1522 if (!ex_command(k
== 'j' ? "b +" : "b -"))
1527 if (!ex_command(k
== 'J' ? "next" : "prev"))
1531 if (!ex_command("b !"))
1538 if (k
== '~' || k
== 'u' || k
== 'U')
1544 if (!vc_definition(0))
1546 if (k
== 'f' || k
== 'l')
1547 if (!vc_openpath(k
== 'l', 0))
1552 if (!vc_motion('d'))
1556 vi_back(TK_CTL('h'));
1557 if (!vc_motion('d'))
1562 if (!vc_motion('c'))
1567 if (!vc_motion('d'))
1572 if (isalpha(k
) && !vc_ecmd(k
, 0))
1581 if (!vc_motion('c'))
1586 if (!vc_motion('c'))
1596 if (!ex_command("x"))
1601 if (!vc_motion('~'))
1614 if (strchr("!<>ACDIJOPRSXYacdioprsxy~", c
) ||
1615 (c
== 'g' && strchr("uU~", k
))) {
1616 if (n
+ 1 < sizeof(rep_cmd
)) {
1617 memcpy(rep_cmd
, cmd
, n
);
1620 reg_put('.', rep_cmd
, 0);
1626 xcol
= vi_off2col(xb
, xrow
, xoff
);
1627 if (xcol
>= xleft
+ xcols
)
1628 xleft
= xcol
- xcols
/ 2;
1630 xleft
= xcol
< xcols
? 0 : xcol
- xcols
/ 2;
1632 ru
= (xru
& 1) || ((xru
& 2) && w_cnt
> 1) || ((xru
& 4) && opath
!= ex_path());
1633 if (mod
& 4 && w_cnt
> 1) {
1634 char msg
[sizeof(vi_msg
)];
1639 strcpy(msg
, vi_msg
);
1642 vi_drawagain(vi_off2col(xb
, xrow
, xoff
), 0);
1643 strcpy(vi_msg
, msg
);
1647 if (ru
&& !vi_msg
[0])
1649 if (mod
|| xleft
!= oleft
) {
1650 vi_drawagain(xcol
, mod
== 2 && xleft
== oleft
&& xrow
== orow
);
1653 vi_drawupdate(xcol
, otop
);
1654 if (xhll
&& xrow
!= orow
&& orow
>= xtop
&& orow
< xtop
+ xrows
)
1656 if (xhll
&& xrow
!= orow
)
1661 term_pos(xrow
- xtop
, led_pos(lbuf_get(xb
, xrow
),
1662 ren_cursor(lbuf_get(xb
, xrow
), xcol
)));
1667 int main(int argc
, char *argv
[])
1670 char *prog
= strchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
1671 xvis
= strcmp("ex", prog
) && strcmp("neatex", prog
);
1672 for (i
= 1; i
< argc
&& argv
[i
][0] == '-'; i
++) {
1673 if (argv
[i
][1] == 's')
1675 if (argv
[i
][1] == 'e')
1677 if (argv
[i
][1] == 'v')
1679 if (argv
[i
][1] == 'h') {
1680 printf("usage: %s [options] [file...]\n\n", argv
[0]);
1681 printf("options:\n");
1682 printf(" -v start in vi mode\n");
1683 printf(" -e start in ex mode\n");
1684 printf(" -s silent mode (for ex mode only)\n");
1690 tag_init(getenv("TAGPATH") ? getenv("TAGPATH") : "tags");
1691 if (!ex_init(argv
+ i
)) {