4 #include "text-motions.h"
5 #include "text-objects.h"
9 static size_t op_delete(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
10 c
->reg
->linewise
= c
->linewise
;
11 register_put(c
->reg
, txt
, &c
->range
);
12 text_delete_range(txt
, &c
->range
);
13 size_t pos
= c
->range
.start
;
14 if (c
->linewise
&& pos
== text_size(txt
))
15 pos
= text_line_begin(txt
, text_line_prev(txt
, pos
));
19 static size_t op_change(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
20 op_delete(vis
, txt
, c
);
21 macro_operator_record(vis
);
22 return c
->range
.start
;
25 static size_t op_yank(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
26 c
->reg
->linewise
= c
->linewise
;
27 register_put(c
->reg
, txt
, &c
->range
);
31 static size_t op_put(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
33 bool sel
= text_range_size(&c
->range
) > 0;
34 bool sel_linewise
= sel
&& text_range_is_linewise(txt
, &c
->range
);
36 text_delete_range(txt
, &c
->range
);
37 pos
= c
->pos
= c
->range
.start
;
40 case VIS_OP_PUT_AFTER
:
41 case VIS_OP_PUT_AFTER_END
:
42 if (c
->reg
->linewise
&& !sel_linewise
)
43 pos
= text_line_next(txt
, pos
);
45 pos
= text_char_next(txt
, pos
);
47 case VIS_OP_PUT_BEFORE
:
48 case VIS_OP_PUT_BEFORE_END
:
50 pos
= text_line_begin(txt
, pos
);
54 for (int i
= 0; i
< c
->count
; i
++) {
55 text_insert(txt
, pos
, c
->reg
->data
, c
->reg
->len
);
59 if (c
->reg
->linewise
) {
61 case VIS_OP_PUT_BEFORE_END
:
62 case VIS_OP_PUT_AFTER_END
:
63 pos
= text_line_start(txt
, pos
);
65 case VIS_OP_PUT_AFTER
:
66 pos
= text_line_start(txt
, text_line_next(txt
, c
->pos
));
68 case VIS_OP_PUT_BEFORE
:
69 pos
= text_line_start(txt
, c
->pos
);
74 case VIS_OP_PUT_AFTER
:
75 case VIS_OP_PUT_BEFORE
:
76 pos
= text_char_prev(txt
, pos
);
84 static size_t op_shift_right(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
85 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
86 const char *tab
= expandtab(vis
);
87 size_t tablen
= strlen(tab
);
89 /* if range ends at the begin of a line, skip line break */
90 if (pos
== c
->range
.end
)
91 pos
= text_line_prev(txt
, pos
);
94 prev_pos
= pos
= text_line_begin(txt
, pos
);
95 text_insert(txt
, pos
, tab
, tablen
);
96 pos
= text_line_prev(txt
, pos
);
97 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
99 return c
->pos
+ tablen
;
102 static size_t op_shift_left(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
103 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
104 size_t tabwidth
= vis
->tabwidth
, tablen
;
106 /* if range ends at the begin of a line, skip line break */
107 if (pos
== c
->range
.end
)
108 pos
= text_line_prev(txt
, pos
);
113 prev_pos
= pos
= text_line_begin(txt
, pos
);
114 Iterator it
= text_iterator_get(txt
, pos
);
115 if (text_iterator_byte_get(&it
, &c
) && c
== '\t') {
118 for (len
= 0; text_iterator_byte_get(&it
, &c
) && c
== ' '; len
++)
119 text_iterator_byte_next(&it
, NULL
);
121 tablen
= MIN(len
, tabwidth
);
122 text_delete(txt
, pos
, tablen
);
123 pos
= text_line_prev(txt
, pos
);
124 } while (pos
>= c
->range
.start
&& pos
!= prev_pos
);
126 return c
->pos
- tablen
;
129 static size_t op_case_change(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
130 size_t len
= text_range_size(&c
->range
);
131 char *buf
= malloc(len
);
134 len
= text_bytes_get(txt
, c
->range
.start
, len
, buf
);
136 for (char *cur
= buf
; rem
> 0; cur
++, rem
--) {
137 int ch
= (unsigned char)*cur
;
139 if (c
->arg
->i
== VIS_OP_CASE_SWAP
)
140 *cur
= islower(ch
) ? toupper(ch
) : tolower(ch
);
141 else if (c
->arg
->i
== VIS_OP_CASE_UPPER
)
148 text_delete(txt
, c
->range
.start
, len
);
149 text_insert(txt
, c
->range
.start
, buf
, len
);
154 static size_t op_cursor(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
155 View
*view
= vis
->win
->view
;
156 Filerange r
= text_range_linewise(txt
, &c
->range
);
157 for (size_t line
= text_range_line_first(txt
, &r
); line
!= EPOS
; line
= text_range_line_next(txt
, &r
, line
)) {
158 Cursor
*cursor
= view_cursors_new(view
);
161 if (c
->arg
->i
== VIS_OP_CURSOR_EOL
)
162 pos
= text_line_finish(txt
, line
);
164 pos
= text_line_start(txt
, line
);
165 view_cursors_to(cursor
, pos
);
171 static size_t op_join(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
172 size_t pos
= text_line_begin(txt
, c
->range
.end
), prev_pos
;
174 /* if operator and range are both linewise, skip last line break */
175 if (c
->linewise
&& text_range_is_linewise(txt
, &c
->range
)) {
176 size_t line_prev
= text_line_prev(txt
, pos
);
177 size_t line_prev_prev
= text_line_prev(txt
, line_prev
);
178 if (line_prev_prev
>= c
->range
.start
)
184 size_t end
= text_line_start(txt
, pos
);
185 pos
= text_char_next(txt
, text_line_finish(txt
, text_line_prev(txt
, end
)));
186 if (pos
>= c
->range
.start
&& end
> pos
) {
187 text_delete(txt
, pos
, end
- pos
);
188 text_insert(txt
, pos
, " ", 1);
192 } while (pos
!= prev_pos
);
194 return c
->range
.start
;
197 static size_t op_insert(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
198 macro_operator_record(vis
);
199 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
202 static size_t op_replace(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
203 macro_operator_record(vis
);
204 return c
->newpos
!= EPOS
? c
->newpos
: c
->pos
;
207 static size_t op_filter(Vis
*vis
, Text
*txt
, OperatorContext
*c
) {
209 macro_operator_record(vis
);
210 return text_size(txt
) + 1; /* do not change cursor position, would destroy selection */
214 [VIS_OP_DELETE
] = { op_delete
},
215 [VIS_OP_CHANGE
] = { op_change
},
216 [VIS_OP_YANK
] = { op_yank
},
217 [VIS_OP_PUT_AFTER
] = { op_put
},
218 [VIS_OP_SHIFT_RIGHT
] = { op_shift_right
},
219 [VIS_OP_SHIFT_LEFT
] = { op_shift_left
},
220 [VIS_OP_CASE_SWAP
] = { op_case_change
},
221 [VIS_OP_JOIN
] = { op_join
},
222 [VIS_OP_INSERT
] = { op_insert
},
223 [VIS_OP_REPLACE
] = { op_replace
},
224 [VIS_OP_CURSOR_SOL
] = { op_cursor
},
225 [VIS_OP_FILTER
] = { op_filter
},